206 lines
4.5 KiB
Go
206 lines
4.5 KiB
Go
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
|
|
}
|