Make the storage backend modular

This commit is contained in:
ChronosX88 2022-01-19 22:51:08 +03:00
parent 99061051e7
commit 3933abd40b
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
9 changed files with 118 additions and 49 deletions

View File

@ -30,11 +30,11 @@ func main() {
log.Printf("Starting %s...", common.ServerName)
ns, err := server.NewNNTPServer(cfg)
if err != nil {
log.Fatal(err)
log.Fatalf("Error occurred while starting the server: %s", err)
}
if err := ns.Start(); err != nil {
log.Fatal(err)
log.Fatalf("Error occurred while starting the server: %s", err)
}
log.Printf("%s has been successfully started!", common.ServerName)
log.Printf("Version: %s", common.ServerVersion)

6
config.sample.toml Normal file
View File

@ -0,0 +1,6 @@
address = "localhost"
port = 1119
backend_type = "sqlite"
[sqlite]
path = "yans.db"

View File

@ -0,0 +1,47 @@
package sqlite
import (
"embed"
"github.com/ChronosX88/yans/internal/config"
"github.com/ChronosX88/yans/internal/models"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"github.com/pressly/goose/v3"
)
//go:embed migrations/*.sql
var migrations embed.FS
type SQLiteBackend struct {
db *sqlx.DB
}
func NewSQLiteBackend(cfg config.SQLiteBackendConfig) (*SQLiteBackend, error) {
db, err := sqlx.Open("sqlite3", cfg.Path)
if err != nil {
return nil, err
}
goose.SetBaseFS(migrations)
if err := goose.SetDialect("sqlite3"); err != nil {
return nil, err
}
if err := goose.Up(db.DB, "migrations"); err != nil {
return nil, err
}
return &SQLiteBackend{
db: db,
}, nil
}
func (sb *SQLiteBackend) ListGroups() ([]models.Group, error) {
var groups []models.Group
return groups, sb.db.Select(&groups, "SELECT * FROM groups")
}
func (sb *SQLiteBackend) GetArticlesCount(g models.Group) (int, error) {
var count int
return count, sb.db.Select(&count, "SELECT COUNT(*) FROM articles_to_groups WHERE group_id = ?", g.ID)
}

View File

@ -0,0 +1,12 @@
package backend
import "github.com/ChronosX88/yans/internal/models"
const (
SupportedBackendList = "sqlite"
)
type StorageBackend interface {
ListGroups() ([]models.Group, error)
GetArticlesCount(g models.Group) (int, error)
}

View File

@ -5,9 +5,19 @@ import (
"os"
)
const (
SQLiteBackendType = "sqlite"
)
type Config struct {
Port int
DatabasePath string
Address string `toml:"address"`
Port int `toml:"port"`
BackendType string `toml:"backend_type"`
SQLite SQLiteBackendConfig `toml:"sqlite"`
}
type SQLiteBackendConfig struct {
Path string `toml:"path"`
}
func ParseConfig(path string) (Config, error) {

View File

@ -1,6 +0,0 @@
package internal
import "embed"
//go:embed migrations/*.sql
var Migrations embed.FS

View File

@ -2,21 +2,20 @@ package server
import (
"fmt"
"github.com/ChronosX88/yans/internal/models"
"github.com/ChronosX88/yans/internal/backend"
"github.com/ChronosX88/yans/internal/protocol"
"github.com/jmoiron/sqlx"
"strings"
"time"
)
type Handler struct {
handlers map[string]func(s *Session, arguments []string) error
db *sqlx.DB
backend backend.StorageBackend
}
func NewHandler(db *sqlx.DB) *Handler {
func NewHandler(b backend.StorageBackend) *Handler {
h := &Handler{}
h.db = db
h.backend = b
h.handlers = map[string]func(s *Session, arguments []string) error{
protocol.CommandCapabilities: h.handleCapabilities,
protocol.CommandDate: h.handleDate,
@ -54,7 +53,7 @@ func (h *Handler) handleList(s *Session, arguments []string) error {
fallthrough
case "ACTIVE":
{
groups, err := h.listGroups()
groups, err := h.backend.ListGroups()
if err != nil {
return err
}
@ -66,7 +65,7 @@ func (h *Handler) handleList(s *Session, arguments []string) error {
}
case "NEWSGROUPS":
{
groups, err := h.listGroups()
groups, err := h.backend.ListGroups()
if err != nil {
return err
}
@ -117,14 +116,3 @@ func (h *Handler) Handle(s *Session, message string) error {
}
return handler(s, splittedMessage[1:])
}
// TODO Refactor to "storage backend" entity
func (h *Handler) listGroups() ([]models.Group, error) {
var groups []models.Group
return groups, h.db.Select(&groups, "SELECT * FROM groups")
}
func (h *Handler) getArticlesCount(g models.Group) (int, error) {
var count int
return count, h.db.Select(&count, "SELECT COUNT(*) FROM articles_to_groups WHERE group_id = ?", g.ID)
}

View File

@ -3,14 +3,12 @@ package server
import (
"context"
"fmt"
"github.com/ChronosX88/yans/internal"
"github.com/ChronosX88/yans/internal/backend"
"github.com/ChronosX88/yans/internal/backend/sqlite"
"github.com/ChronosX88/yans/internal/common"
"github.com/ChronosX88/yans/internal/config"
"github.com/ChronosX88/yans/internal/protocol"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"github.com/pressly/goose/v3"
"log"
"net"
"sync"
@ -29,46 +27,60 @@ type NNTPServer struct {
cancelFunc context.CancelFunc
ln net.Listener
port int
cfg config.Config
db *sqlx.DB
backend backend.StorageBackend
sessionPool map[string]*Session
sessionPoolMutex sync.Mutex
}
func NewNNTPServer(cfg config.Config) (*NNTPServer, error) {
db, err := sqlx.Open("sqlite3", cfg.DatabasePath)
b, err := initBackend(cfg)
if err != nil {
return nil, err
}
goose.SetBaseFS(internal.Migrations)
if err := goose.SetDialect("sqlite3"); err != nil {
return nil, err
}
if err := goose.Up(db.DB, "migrations"); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
ns := &NNTPServer{
ctx: ctx,
cancelFunc: cancel,
port: cfg.Port,
db: db,
cfg: cfg,
backend: b,
sessionPool: map[string]*Session{},
}
return ns, nil
}
func initBackend(cfg config.Config) (backend.StorageBackend, error) {
var sb backend.StorageBackend
switch cfg.BackendType {
case config.SQLiteBackendType:
{
sqliteBackend, err := sqlite.NewSQLiteBackend(cfg.SQLite)
if err != nil {
return nil, err
}
sb = sqliteBackend
}
default:
{
return nil, fmt.Errorf("invalid backend type, supported backends: %s", backend.SupportedBackendList)
}
}
return sb, nil
}
func (ns *NNTPServer) Start() error {
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", ns.port))
address := fmt.Sprintf("%s:%d", ns.cfg.Address, ns.cfg.Port)
ln, err := net.Listen("tcp", address)
if err != nil {
return err
}
log.Printf("Listening on %s...", address)
go func(ctx context.Context) {
for {
select {
@ -84,7 +96,7 @@ func (ns *NNTPServer) Start() error {
id, _ := uuid.NewUUID()
closed := make(chan bool)
session, err := NewSession(ctx, conn, Capabilities, id.String(), closed, NewHandler(ns.db))
session, err := NewSession(ctx, conn, Capabilities, id.String(), closed, NewHandler(ns.backend))
ns.sessionPoolMutex.Lock()
ns.sessionPool[id.String()] = session
ns.sessionPoolMutex.Unlock()