mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 10:19:07 +00:00
Here's the commit message: ``` Fix code style and indentation Apply consistent indentation, fix whitespace and tabs vs spaces issues, remove trailing whitespace, and ensure proper line endings throughout the codebase. Also add comments and improve code organization. ``` The message body is included since this is a bigger cleanup effort that touched multiple files and made various formatting improvements that are worth explaining.
143 lines
4.4 KiB
Go
143 lines
4.4 KiB
Go
package gateway
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.debros.io/DeBros/network/pkg/client"
|
|
"git.debros.io/DeBros/network/pkg/logging"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Config holds configuration for the gateway server
|
|
type Config struct {
|
|
ListenAddr string
|
|
ClientNamespace string
|
|
BootstrapPeers []string
|
|
}
|
|
|
|
type Gateway struct {
|
|
logger *logging.ColoredLogger
|
|
cfg *Config
|
|
client client.NetworkClient
|
|
startedAt time.Time
|
|
signingKey *rsa.PrivateKey
|
|
keyID string
|
|
}
|
|
|
|
// New creates and initializes a new Gateway instance
|
|
func New(logger *logging.ColoredLogger, cfg *Config) (*Gateway, error) {
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Building client config...")
|
|
|
|
// Build client config from gateway cfg
|
|
cliCfg := client.DefaultClientConfig(cfg.ClientNamespace)
|
|
if len(cfg.BootstrapPeers) > 0 {
|
|
cliCfg.BootstrapPeers = cfg.BootstrapPeers
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Creating network client...")
|
|
c, err := client.NewClient(cliCfg)
|
|
if err != nil {
|
|
logger.ComponentError(logging.ComponentClient, "failed to create network client", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Connecting network client...")
|
|
if err := c.Connect(); err != nil {
|
|
logger.ComponentError(logging.ComponentClient, "failed to connect network client", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentClient, "Network client connected",
|
|
zap.String("namespace", cliCfg.AppName),
|
|
zap.Int("bootstrap_peer_count", len(cliCfg.BootstrapPeers)),
|
|
)
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Creating gateway instance...")
|
|
gw := &Gateway{
|
|
logger: logger,
|
|
cfg: cfg,
|
|
client: c,
|
|
startedAt: time.Now(),
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Generating RSA signing key...")
|
|
// Generate local RSA signing key for JWKS/JWT (ephemeral for now)
|
|
if key, err := rsa.GenerateKey(rand.Reader, 2048); err == nil {
|
|
gw.signingKey = key
|
|
gw.keyID = "gw-" + strconv.FormatInt(time.Now().Unix(), 10)
|
|
logger.ComponentInfo(logging.ComponentGeneral, "RSA key generated successfully")
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentGeneral, "failed to generate RSA key; jwks will be empty", zap.Error(err))
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Starting database migrations goroutine...")
|
|
// Non-blocking DB migrations: probe RQLite; if reachable, apply migrations asynchronously
|
|
go func() {
|
|
if gw.probeRQLiteReachable(3 * time.Second) {
|
|
internalCtx := gw.withInternalAuth(context.Background())
|
|
if err := gw.applyMigrations(internalCtx); err != nil {
|
|
if err == errNoMigrationsFound {
|
|
if err2 := gw.applyAutoMigrations(internalCtx); err2 != nil {
|
|
logger.ComponentWarn(logging.ComponentDatabase, "auto migrations failed", zap.Error(err2))
|
|
} else {
|
|
logger.ComponentInfo(logging.ComponentDatabase, "auto migrations applied")
|
|
}
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentDatabase, "migrations failed", zap.Error(err))
|
|
}
|
|
} else {
|
|
logger.ComponentInfo(logging.ComponentDatabase, "migrations applied")
|
|
}
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentDatabase, "RQLite not reachable; skipping migrations for now")
|
|
}
|
|
}()
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Gateway creation completed, returning...")
|
|
return gw, nil
|
|
}
|
|
|
|
// withInternalAuth creates a context for internal gateway operations that bypass authentication
|
|
func (g *Gateway) withInternalAuth(ctx context.Context) context.Context {
|
|
return client.WithInternalAuth(ctx)
|
|
}
|
|
|
|
// probeRQLiteReachable performs a quick GET /status against candidate endpoints with a short timeout.
|
|
func (g *Gateway) probeRQLiteReachable(timeout time.Duration) bool {
|
|
endpoints := client.DefaultDatabaseEndpoints()
|
|
httpClient := &http.Client{Timeout: timeout}
|
|
for _, ep := range endpoints {
|
|
url := ep
|
|
if url == "" {
|
|
continue
|
|
}
|
|
if url[len(url)-1] == '/' {
|
|
url = url[:len(url)-1]
|
|
}
|
|
reqURL := url + "/status"
|
|
resp, err := httpClient.Get(reqURL)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
resp.Body.Close()
|
|
if resp.StatusCode == http.StatusOK {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Close disconnects the gateway client
|
|
func (g *Gateway) Close() {
|
|
if g.client != nil {
|
|
if err := g.client.Disconnect(); err != nil {
|
|
g.logger.ComponentWarn(logging.ComponentClient, "error during client disconnect", zap.Error(err))
|
|
}
|
|
}
|
|
}
|