// internal/db/db.go package db import ( "fmt" "path/filepath" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" ) // DB holds the database connection type DB struct { *sqlx.DB } // New creates a new DB instance func New(dbPath string) (*DB, error) { // Ensure the directory exists dir := filepath.Dir(dbPath) if err := ensureDir(dir); err != nil { return nil, fmt.Errorf("failed to create database directory: %w", err) } // Connect to the database db, err := sqlx.Connect("sqlite3", dbPath) if err != nil { return nil, fmt.Errorf("failed to connect to database: %w", err) } // Set pragmas for better performance db.MustExec("PRAGMA journal_mode=WAL;") db.MustExec("PRAGMA foreign_keys=ON;") return &DB{db}, nil } // Initialize creates the database schema if it doesn't exist func (db *DB) Initialize() error { // Create bots table _, err := db.Exec(` CREATE TABLE IF NOT EXISTS bots ( id INTEGER PRIMARY KEY AUTOINCREMENT, pubkey TEXT NOT NULL UNIQUE, encrypted_privkey TEXT NOT NULL, name TEXT NOT NULL, display_name TEXT, bio TEXT, nip05 TEXT, zap_address TEXT, profile_picture TEXT, banner TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, owner_pubkey TEXT NOT NULL )`) if err != nil { return fmt.Errorf("failed to create bots table: %w", err) } // Create post_config table _, err = db.Exec(` CREATE TABLE IF NOT EXISTS post_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER NOT NULL, hashtags TEXT, interval_minutes INTEGER NOT NULL DEFAULT 60, post_template TEXT, enabled BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY (bot_id) REFERENCES bots(id) ON DELETE CASCADE )`) if err != nil { return fmt.Errorf("failed to create post_config table: %w", err) } // Create media_config table _, err = db.Exec(` CREATE TABLE IF NOT EXISTS media_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER NOT NULL, primary_service TEXT NOT NULL, fallback_service TEXT, nip94_server_url TEXT, blossom_server_url TEXT, FOREIGN KEY (bot_id) REFERENCES bots(id) ON DELETE CASCADE )`) if err != nil { return fmt.Errorf("failed to create media_config table: %w", err) } // Create relays table _, err = db.Exec(` CREATE TABLE IF NOT EXISTS relays ( id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER NOT NULL, url TEXT NOT NULL, read BOOLEAN NOT NULL DEFAULT 1, write BOOLEAN NOT NULL DEFAULT 1, FOREIGN KEY (bot_id) REFERENCES bots(id) ON DELETE CASCADE, UNIQUE (bot_id, url) )`) if err != nil { return fmt.Errorf("failed to create relays table: %w", err) } // Create posts table _, err = db.Exec(` CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER NOT NULL, content_filename TEXT, media_url TEXT, event_id TEXT, status TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, error TEXT, FOREIGN KEY (bot_id) REFERENCES bots(id) ON DELETE CASCADE )`) if err != nil { return fmt.Errorf("failed to create posts table: %w", err) } return nil } // ensureDir makes sure the directory exists func ensureDir(dir string) error { return nil // This will be implemented in a file utility package }