package node import ( "context" "crypto/ed25519" "encoding/base64" "fmt" "log/slog" "net" "os" "strings" "koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity" "koti.casa/numenor-labs/dsfx/internal/lib/disk" "koti.casa/numenor-labs/dsfx/internal/lib/logging" "koti.casa/numenor-labs/dsfx/internal/lib/network" "koti.casa/numenor-labs/dsfx/internal/lib/system" "koti.casa/numenor-labs/dsfx/internal/peer/conf" ) type Node struct { config disk.Disk system system.System conf conf.Conf } func New(config disk.Disk, system system.System) *Node { conf := conf.FromSystem(system) return &Node{config, system, conf} } func (a *Node) Run(ctx context.Context) error { opts := &slog.HandlerOptions{ AddSource: false, Level: slog.LevelDebug, } logger := slog.New(slog.NewJSONHandler(a.system.Stdout(), opts)) // Everything in the application will attempt to use the logger in stored in // the context, but we also set the default with slog as a fallback. In cases // where the context is not available, or the context is not a child of the // context with the logger, the default logger will be used. slog.SetDefault(logger) ctx = logging.WithContext(ctx, logger) ki := &KeyInit{disk: a.config} // Check if the key file exists and is not empty hasKey, err := ki.Has() if err != nil { logger.ErrorContext(ctx, "failed to check key file", slog.Any("error", err)) return err } if !hasKey { logger.InfoContext(ctx, "key file does not exist or is empty, generating new key") err = ki.Init() if err != nil { logger.ErrorContext(ctx, "failed to initialize key file", slog.Any("error", err)) return err } } // Read the key file id, err := ki.Read() if err != nil { logger.ErrorContext(ctx, "failed to read key file", slog.Any("error", err)) return err } admins := []string{} ai := &AdminInit{disk: a.config} // Check if the admins file exists and is not empty hasAdmins, err := ai.Has() if err != nil { logger.ErrorContext(ctx, "failed to check admins file", slog.Any("error", err)) return err } if hasAdmins { logger.InfoContext(ctx, "admins file exists, reading admins") admins, err = ai.Read() if err != nil { logger.ErrorContext(ctx, "failed to read admins file", slog.Any("error", err)) return err } logger.InfoContext(ctx, "loaded admins", slog.Any("admins", admins)) } else { logger.WarnContext(ctx, "admins file does not exist or is empty, no admins will be loaded") } tcpAddrRaw := net.JoinHostPort(a.conf.Host, a.conf.Port) tcpAddr, err := net.ResolveTCPAddr("tcp", tcpAddrRaw) if err != nil { return err } addr := network.FromTCPAddr(tcpAddr, id.Public().(ed25519.PublicKey)) // --------------------------------------------------------------------------- // Listener listener, err := network.Listen(ctx, id, addr) if err != nil { logger.ErrorContext(ctx, "listener error", slog.Any("error", err)) return err } for { conn, err := listener.Accept() if err != nil { logger.ErrorContext(ctx, "accept failure", slog.Any("error", err)) continue } go handleConnection(ctx, conn) } } func handleConnection(ctx context.Context, conn net.Conn) error { defer conn.Close() logger := logging.FromContext(ctx) msg := make([]byte, 1024) n, err := conn.Read(msg) if err != nil { logger.ErrorContext(ctx, "failed to read from connection", slog.Any("error", err)) return err } logger.InfoContext(ctx, "received msg", slog.Int("bytes", n)) return nil } type KeyInit struct{ disk disk.Disk } func (ki *KeyInit) Has() (bool, error) { f, err := ki.disk.Open("key") if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } defer f.Close() stats, err := f.Stat() if err != nil { return false, err } return stats.Size() > 0, nil } func (ki *KeyInit) Init() error { f, err := ki.disk.Create("key") if err != nil { return err } defer f.Close() privkey, err := identity.Generate() if err != nil { return err } _, err = f.Write([]byte(base64.StdEncoding.EncodeToString(privkey))) if err != nil { return err } return nil } func (ki *KeyInit) Read() (ed25519.PrivateKey, error) { f, err := ki.disk.Open("key") if err != nil { return nil, err } defer f.Close() keyRaw := make([]byte, ed25519.PrivateKeySize) n, err := f.Read(keyRaw) if err != nil { return nil, err } if n != ed25519.PrivateKeySize { return nil, fmt.Errorf("key file is not the correct size: %d", n) } return ed25519.PrivateKey(keyRaw), nil } type AdminInit struct { disk disk.Disk } func (ai *AdminInit) Has() (bool, error) { f, err := ai.disk.Open("admins") if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } defer f.Close() stats, err := f.Stat() if err != nil { return false, err } return stats.Size() > 0, nil } func (ai *AdminInit) Read() ([]string, error) { f, err := ai.disk.Open("admins") if err != nil { return nil, err } defer f.Close() adminsRaw := make([]byte, 0) n, err := f.Read(adminsRaw) if err != nil { return nil, err } if n == 0 { return nil, fmt.Errorf("admins file is empty") } rawAdmins := strings.Split(string(adminsRaw), "\n") var admins []string for _, admin := range rawAdmins { if admin != "" { admins = append(admins, admin) } } return admins, nil }