package server import ( "context" "encoding/hex" "fmt" "log/slog" "net" "sync" "go.neonxp.ru/qchat/internal/chat" "go.neonxp.ru/qchat/internal/config" "golang.org/x/crypto/ssh" ) type Server struct { cfg *config.Config chat *chat.Chat } func New(cfg *config.Config, ch *chat.Chat) *Server { return &Server{ cfg: cfg, chat: ch, } } func (s *Server) Run(ctx context.Context) error { config := &ssh.ServerConfig{ PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { return &ssh.Permissions{ ExtraData: map[any]any{ "identify": hex.EncodeToString(pubKey.Marshal()), }, }, nil }, } private, err := ssh.ParsePrivateKey([]byte(s.cfg.Server.PrivateKey)) if err != nil { return fmt.Errorf("failed to parse private key: %w", err) } config.AddHostKey(private) listener, err := net.Listen("tcp", s.cfg.Server.Addr) if err != nil { return fmt.Errorf("failed to parse private key: %w", err) } go func() { <-ctx.Done() _ = listener.Close() }() wg := sync.WaitGroup{} defer wg.Wait() slog.InfoContext(ctx, "started server at", slog.String("addr", s.cfg.Server.Addr)) for { nConn, err := listener.Accept() if err != nil { slog.ErrorContext(ctx, "failed to accept incoming connection", slog.Any("err", err)) continue } wg.Go(func() { if err := s.serveConn(ctx, nConn, config); err != nil { slog.ErrorContext(ctx, "connection error", slog.Any("err", err)) } }) } }