Files
ax/db/db.go

84 lines
2.3 KiB
Go
Raw Normal View History

2026-03-26 12:48:47 +00:00
package db
import (
"database/sql"
"errors"
"fmt"
"os"
"os/user"
"path/filepath"
_ "modernc.org/sqlite"
)
type DB struct {
*sql.DB
path string
}
var (
database *DB
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 TABLE IF NOT EXISTS aliases (name TEXT PRIMARY KEY, command TEXT NOT NULL)`,
`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)`,
}
)
2026-03-26 12:48:47 +00:00
func GetDB() (*DB, error) {
if database != nil {
return database, nil
}
dir, err := filepath.Abs(".")
if err != nil {
return nil, err
}
for {
if _, err := os.Stat(filepath.Join(dir, ".ax.db")); err == nil {
if database, err = Open(filepath.Join(dir, ".ax.db")); err != nil {
2026-03-26 12:48:47 +00:00
return nil, fmt.Errorf("failed to open database: %w", err)
}
return database, nil
}
if parent := filepath.Dir(dir); parent == dir {
2026-03-26 12:48:47 +00:00
return nil, errors.New("no .ax.db found (run 'ax init' first)")
} else {
dir = parent
2026-03-26 12:48:47 +00:00
}
}
}
func Init(path string) error {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
2026-03-26 12:48:47 +00:00
return err
}
var err error
database, err = Open(path)
return err
}
func Open(path string) (*DB, error) {
db, err := sql.Open("sqlite", path)
2026-03-26 12:48:47 +00:00
if err != nil {
return nil, 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 {
2026-03-26 12:48:47 +00:00
return nil, err
}
}
return &DB{DB: db, path: path}, nil
2026-03-26 12:48:47 +00:00
}
func GetCurrentUser() string {
if u := os.Getenv("AX_USER"); u != "" {
return u
}
if u, err := user.Current(); err == nil {
return u.Username
}
return "unknown"
}