From 77ac83e700415cdbd4635eae91e79f99312ea240 Mon Sep 17 00:00:00 2001 From: tjpcc Date: Wed, 30 Aug 2023 10:29:47 -0600 Subject: Initial commit * iris support copied in from iris-news * a new slog backend * "metabackend" wraps and routes between multiple backends based on the groups they support * better logging than iris-news ever had --- backend.go | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 backend.go (limited to 'backend.go') diff --git a/backend.go b/backend.go new file mode 100644 index 0000000..0291ffa --- /dev/null +++ b/backend.go @@ -0,0 +1,205 @@ +package main + +import ( + "errors" + "strconv" + "strings" + + "github.com/dustin/go-nntp" + nntpserver "github.com/dustin/go-nntp/server" + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +func NewMetaBackend(logger log.Logger, backends ...nntpserver.Backend) (nntpserver.Backend, error) { + mb := &metaBackend{logger: logger, groups: make(map[string]int)} + for _, b := range backends { + if err := mb.add(b); err != nil { + return nil, err + } + } + + return mb, nil +} + +type metaBackend struct { + logger log.Logger + backends []nntpserver.Backend + groups map[string]int +} + +func (mb metaBackend) debug(keyvals ...any) error { return level.Debug(mb.logger).Log(keyvals...) } +func (mb metaBackend) info(keyvals ...any) error { return level.Info(mb.logger).Log(keyvals...) } +func (mb metaBackend) warn(keyvals ...any) error { return level.Warn(mb.logger).Log(keyvals...) } +func (mb metaBackend) err(keyvals ...any) error { return level.Error(mb.logger).Log(keyvals...) } + +func (mb *metaBackend) add(b nntpserver.Backend) error { + grps, err := b.ListGroups(1_000_000) + if err != nil { + return err + } + + _ = mb.info("msg", "adding backend", "group_count", len(grps)) + + i := len(mb.backends) + mb.backends = append(mb.backends, b) + for _, grp := range grps { + mb.groups[grp.Name] = i + } + + return nil +} + +func (mb metaBackend) ListGroups(max int) ([]*nntp.Group, error) { + var groups []*nntp.Group + for _, b := range mb.backends { + grps, err := b.ListGroups(max - len(groups)) + if err != nil { + return nil, err + } + groups = append(groups, grps...) + + if len(groups) == max { + break + } + } + + _ = mb.info( + "msg", "metaBackend method", + "method", "ListGroups", + "count", len(groups), + ) + return groups, nil +} + +func (mb metaBackend) GetGroup(name string) (*nntp.Group, error) { + b, err := mb.backendFor(name) + if err != nil { + return nil, err + } + + grp, err := b.GetGroup(name) + _ = mb.info( + "msg", "metaBackend method", + "method", "GetGroup", + "name", name, + "err", err, + ) + return grp, err +} + +func (mb metaBackend) GetArticles(group *nntp.Group, from, to int64) ([]nntpserver.NumberedArticle, error) { + b, err := mb.backendFor(group.Name) + if err != nil { + return nil, err + } + + articles, err := b.GetArticles(group, from, to) + _ = mb.info( + "msg", "metaBackend method", + "method", "GetArticles", + "group", group.Name, + "from-to", strconv.Itoa(int(from))+"-"+strconv.Itoa(int(to)), + "count", len(articles), + "err", err, + ) + return articles, err +} + +func (mb metaBackend) GetArticle(group *nntp.Group, messageID string) (*nntp.Article, error) { + b, err := mb.backendFor(group.Name) + if err != nil { + return nil, err + } + + article, err := b.GetArticle(group, messageID) + _ = mb.info( + "msg", "metaBackend method", + "method", "GetArticle", + "group", group.Name, + "messageID", messageID, + "err", err, + ) + return article, err +} + +func (mb metaBackend) Post(article *nntp.Article) error { + groupNames := strings.Split(article.Header.Get("Newsgroups"), ",") + for i := range groupNames { + groupNames[i] = strings.Trim(groupNames[i], " ") + } + + bes, err := mb.backendsFor(groupNames) + if err != nil { + return err + } + + var errs []error + for _, b := range bes { + // TODO: need to filter the "Newsgroups" header to only the + // groups relevant to each backend? + errs = append(errs, b.Post(article)) + } + + err = errors.Join(errs...) + _ = mb.info( + "msg", "metaBackend method", + "method", "Post", + "groups", article.Header.Get("Newsgroups"), + "backends", len(bes), + "err", err, + ) + return err +} + +func (mb metaBackend) Authorized() bool { + _ = mb.info( + "msg", "metaBackend method", + "method", "Authorized", + ) + return true +} + +func (mb metaBackend) AllowPost() bool { + _ = mb.info( + "msg", "metaBackend method", + "method", "AllowPost", + ) + return true +} + +func (mb metaBackend) Authenticate(user, _ string) (nntpserver.Backend, error) { + _ = mb.info( + "msg", "metaBackend method", + "method", "Authenticate", + "user", user, + ) + return nil, nil +} + +func (mb metaBackend) backendFor(name string) (nntpserver.Backend, error) { + i, ok := mb.groups[name] + if !ok { + return nil, nntpserver.ErrNoSuchGroup + } + return mb.backends[i], nil +} + +func (mb metaBackend) backendsFor(names []string) ([]nntpserver.Backend, error) { + tbl := make([]bool, len(mb.backends)) + for _, name := range names { + i, ok := mb.groups[name] + if !ok { + return nil, nntpserver.ErrNoSuchGroup + } + tbl[i] = true + } + + backends := make([]nntpserver.Backend, 0, len(mb.backends)) + for i, y := range tbl { + if y { + backends = append(backends, mb.backends[i]) + } + } + return backends, nil +} -- cgit v1.2.3