refactor: remove db package and move database logic to service layer

This commit is contained in:
2026-03-29 21:24:09 +02:00
parent 6ff013dd2a
commit 4ebcb88628
16 changed files with 163 additions and 217 deletions

View File

@@ -3,12 +3,10 @@ package service
import (
"encoding/json"
"errors"
"fmt"
"os"
"os/user"
"path/filepath"
"slices"
"strings"
)
type fileConfig struct {
@@ -18,13 +16,12 @@ type fileConfig struct {
}
var defaultAliases = []*Alias{
{Name: "mine", Command: "list --assignee $me --tag _status::open", Description: "Show open tasks assigned to you"},
{Name: "due", Command: "list --tag _status::open --tag _due", Description: "Show open tasks with due dates"},
{Name: "new", Command: "add $@", Description: "Create a new task"},
{Name: "mine", Command: "list --assignee $me --tag _type::issue --tag _status::open", Description: "Show open issues assigned to you"},
{Name: "due", Command: "list --tag _type::issue --tag _status::open", Description: "Show open issues"},
{Name: "inbox", Command: "list --mention $me", Description: "Show your inbox"},
}
func LoadConfig() (Config, error) {
func LoadConfigFile() (Config, error) {
path, err := findConfigPath()
if err != nil {
return nil, err
@@ -153,34 +150,3 @@ func (c *fileConfig) Save() error {
}
return os.WriteFile(c.path, data, 0644)
}
func ExpandAlias(alias *Alias, args []string, currentUser string) []string {
cmd := alias.Command
cmd = strings.ReplaceAll(cmd, "$me", currentUser)
parts := strings.Fields(cmd)
var result []string
for _, part := range parts {
if part == "$@" {
result = append(result, args...)
continue
}
hasCatchAll := strings.Contains(part, "$@")
replaced := part
if hasCatchAll {
replaced = strings.ReplaceAll(replaced, "$@", strings.Join(args, " "))
}
for i := len(args) - 1; i >= 0; i-- {
placeholder := fmt.Sprintf("$%d", i+1)
replaced = strings.ReplaceAll(replaced, placeholder, args[i])
}
result = append(result, replaced)
}
return result
}

View File

@@ -12,6 +12,18 @@ type NodeService interface {
CanClose(id string) (bool, []string, error)
}
func InitNodeService(path string) error {
return InitSqliteDB(path)
}
func GetNodeService(cfg Config) (NodeService, error) {
db, err := GetSqliteDB(cfg)
if err != nil {
return nil, err
}
return &sqliteNodeService{db: db, userID: cfg.GetUser()}, nil
}
type listFilter struct {
tagPrefixes []string
assignee string

View File

@@ -3,10 +3,16 @@ package service
import (
"axolotl/models"
"database/sql"
"errors"
"fmt"
"math/rand"
"os"
"path/filepath"
"slices"
"strings"
"time"
_ "modernc.org/sqlite"
)
type sqliteNodeService struct {
@@ -14,8 +20,50 @@ type sqliteNodeService struct {
userID string
}
func NewSQLiteNodeService(db *sql.DB, userID string) NodeService {
return &sqliteNodeService{db: db, userID: userID}
var migrations = []string{
`CREATE TABLE IF NOT EXISTS nodes (id TEXT PRIMARY KEY, title TEXT NOT NULL, content TEXT, due_date TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)`,
`CREATE TABLE IF NOT EXISTS tags (node_id TEXT NOT NULL, tag TEXT NOT NULL, PRIMARY KEY (node_id, tag), FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE)`,
`CREATE TABLE IF NOT EXISTS rels (from_id TEXT NOT NULL, to_id TEXT NOT NULL, rel_type TEXT NOT NULL, PRIMARY KEY (from_id, to_id, rel_type), FOREIGN KEY (from_id) REFERENCES nodes(id) ON DELETE CASCADE, FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE)`,
`CREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag)`, `CREATE INDEX IF NOT EXISTS idx_rels_from ON rels(from_id)`, `CREATE INDEX IF NOT EXISTS idx_rels_to ON rels(to_id)`,
}
func InitSqliteDB(path string) error {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
var err error
db, err := sql.Open("sqlite", path)
if err != nil {
return err
}
for _, q := range append([]string{"PRAGMA journal_mode=WAL", "PRAGMA busy_timeout=5000", "PRAGMA foreign_keys=ON"}, migrations...) {
if _, err := db.Exec(q); err != nil {
return err
}
}
return err
}
func GetSqliteDB(cfg Config) (*sql.DB, error) {
dir, err := filepath.Abs(".")
if err != nil {
return nil, err
}
for {
dbpath := filepath.Join(dir, ".ax.db")
if _, err := os.Stat(dbpath); err == nil {
db, err := sql.Open("sqlite", dbpath)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
return db, nil
}
if parent := filepath.Dir(dir); parent == dir {
return nil, errors.New("no .ax.db found (run 'ax init' first)")
} else {
dir = parent
}
}
}
func (s *sqliteNodeService) GetByID(id string) (*models.Node, error) {