50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
_ "embed" // Required for go:embed
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
_ "github.com/mattn/go-sqlite3" // SQLite driver
|
|
)
|
|
|
|
//go:embed schema.sql
|
|
var ddl string
|
|
|
|
// DBConfig holds database configuration.
|
|
type DBConfig struct {
|
|
DSN string // Data Source Name for SQLite
|
|
}
|
|
|
|
// NewDB initializes and returns a new database connection pool and runs migrations.
|
|
func NewDB(ctx context.Context, logger *slog.Logger, dsn string) (*sql.DB, error) {
|
|
// Ensure the directory for the SQLite file exists
|
|
dbDir := filepath.Dir(dsn)
|
|
if err := os.MkdirAll(dbDir, 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create database directory %s: %w", dbDir, err)
|
|
}
|
|
|
|
db, err := sql.Open("sqlite3", dsn+"?_foreign_keys=on") // Enable foreign key constraints
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open database: %w", err)
|
|
}
|
|
|
|
if err = db.PingContext(ctx); err != nil {
|
|
return nil, fmt.Errorf("failed to ping database: %w", err)
|
|
}
|
|
|
|
logger.Info("database connection established", slog.String("dsn", dsn))
|
|
|
|
// Execute schema.
|
|
if _, err := db.ExecContext(ctx, ddl); err != nil {
|
|
return nil, fmt.Errorf("failed to execute DDL: %w", err)
|
|
}
|
|
logger.Info("database schema applied")
|
|
|
|
return db, nil
|
|
}
|