start changes to make into an importable library
parent
74c70e6723
commit
cdc8fc4e5e
|
@ -1,73 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/tursodatabase/go-libsql"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type LibSqlDB struct {
|
||||
db *sql.DB
|
||||
connector *libsql.Connector // only used for embedded replica
|
||||
dir string // only used for embedded replica
|
||||
}
|
||||
|
||||
var syncInterval = 200 * time.Millisecond
|
||||
|
||||
func NewLibSqlDB(primaryUrl, authToken, localDBName string) (*LibSqlDB, error) {
|
||||
dir, err := os.MkdirTemp("", "libsql-*")
|
||||
if err != nil {
|
||||
fmt.Println("Error creating temporary directory:", err)
|
||||
return nil, fmt.Errorf("error setting up temporary directory for local database | %w", err)
|
||||
}
|
||||
//defer os.RemoveAll(dir)
|
||||
|
||||
dbPath := filepath.Join(dir, localDBName)
|
||||
|
||||
connector, err := libsql.NewEmbeddedReplicaConnector(dbPath, primaryUrl,
|
||||
libsql.WithAuthToken(authToken),
|
||||
libsql.WithSyncInterval(syncInterval),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating connector | %w", err)
|
||||
}
|
||||
|
||||
db := sql.OpenDB(connector)
|
||||
|
||||
err = setupMigrations()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up migrations | %w", err)
|
||||
}
|
||||
|
||||
return &LibSqlDB{
|
||||
db: db,
|
||||
connector: connector,
|
||||
dir: dir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *LibSqlDB) Close() error {
|
||||
var resultError *multierror.Error
|
||||
|
||||
if err := t.db.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close database: %w", err))
|
||||
}
|
||||
|
||||
if t.connector != nil {
|
||||
if err := t.connector.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close connector: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
if t.dir != "" {
|
||||
if err := os.RemoveAll(t.dir); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to remove directory %s: %w", t.dir, err))
|
||||
}
|
||||
}
|
||||
|
||||
return resultError.ErrorOrNil()
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
//go:build windows
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql"
|
||||
)
|
||||
|
||||
type LibSqlDB struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewLibSqlDB(primaryUrl, authToken, localDBName string) (*LibSqlDB, error) {
|
||||
url := primaryUrl + "?authToken=" + authToken
|
||||
db, err := sql.Open("libsql", url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up LibSQL db | %w", err)
|
||||
}
|
||||
|
||||
err = setupMigrations()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up migrations | %w", err)
|
||||
}
|
||||
|
||||
return &LibSqlDB{
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *LibSqlDB) Close() error {
|
||||
var resultError *multierror.Error
|
||||
|
||||
if err := t.db.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close database: %w", err))
|
||||
}
|
||||
|
||||
return resultError.ErrorOrNil()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package db
|
||||
package libsqlDB
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Migrations struct {
|
||||
|
@ -13,8 +14,10 @@ type Migrations struct {
|
|||
query string
|
||||
}
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationFiles embed.FS
|
||||
type Options func(*LibSqlDB) error
|
||||
|
||||
// use something like this in the user's code -> //go:embed migrations/*.sql
|
||||
var _migrationFiles embed.FS
|
||||
|
||||
var migrations []Migrations
|
||||
|
||||
|
@ -26,12 +29,12 @@ var migrations []Migrations
|
|||
// setupMigrations initializes the filesystem and reads the migration files into the migrations variable
|
||||
func setupMigrations() error {
|
||||
// Walk through the embedded files and read their contents
|
||||
err := fs.WalkDir(migrationFiles, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
err := fs.WalkDir(_migrationFiles, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsDir() {
|
||||
content, err := migrationFiles.ReadFile(path)
|
||||
content, err := _migrationFiles.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,3 +90,51 @@ func (t *LibSqlDB) Migrate() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithLocalDBName sets the local database name for the embedded database
|
||||
func WithLocalDBName(localDBName string) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.localDBName = localDBName
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSyncInterval sets the sync interval for the embedded database
|
||||
func WithSyncInterval(syncInterval time.Duration) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.syncInterval = syncInterval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDir sets the directory for the embedded database
|
||||
func WithDir(dir string) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.dir = dir
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAuthToken sets the auth token for the database
|
||||
func WithAuthToken(authToken string) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.authToken = authToken
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEncryptionKey sets the encryption key for the embedded database
|
||||
func WithEncryptionKey(key string) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.encryptionKey = key
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithReadYourWrites sets the encryption key for the embedded database
|
||||
func WithReadYourWrites(readYourWrites bool) Options {
|
||||
return func(l *LibSqlDB) error {
|
||||
l.readYourWrites = &readYourWrites
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
//go:build !windows
|
||||
|
||||
package libsqlDB
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type LibSqlDB struct {
|
||||
db *sql.DB
|
||||
connector *libsql.Connector // only used for embedded replica
|
||||
dir string // only used for embedded replica
|
||||
localDBName string // only used for embedded replica
|
||||
authToken string
|
||||
syncInterval time.Duration // only used for embedded replica
|
||||
encryptionKey string // only used for embedded replica
|
||||
readYourWrites *bool // only used for embedded replica
|
||||
}
|
||||
|
||||
var syncInterval = 200 * time.Millisecond
|
||||
|
||||
func NewLibSqlDB(primaryUrl string, migrationFiles embed.FS, opts ...Options) (*LibSqlDB, error) {
|
||||
l := libSqlDefaults()
|
||||
|
||||
for _, option := range opts {
|
||||
err := option(l)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error applying options | %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
dir, err := os.MkdirTemp("", "libsql-*")
|
||||
if err != nil {
|
||||
fmt.Println("Error creating temporary directory:", err)
|
||||
return nil, fmt.Errorf("error setting up temporary directory for local database | %w", err)
|
||||
}
|
||||
//defer os.RemoveAll(dir)
|
||||
|
||||
dbPath := filepath.Join(dir, l.localDBName)
|
||||
|
||||
var lsOpts []libsql.Option
|
||||
|
||||
if l.authToken != "" {
|
||||
lsOpts = append(lsOpts, libsql.WithAuthToken(l.authToken))
|
||||
}
|
||||
|
||||
if l.syncInterval != 0 {
|
||||
lsOpts = append(lsOpts, libsql.WithSyncInterval(l.syncInterval))
|
||||
}
|
||||
|
||||
if l.encryptionKey != "" {
|
||||
lsOpts = append(lsOpts, libsql.WithEncryption(l.encryptionKey))
|
||||
}
|
||||
|
||||
if l.readYourWrites != nil {
|
||||
lsOpts = append(lsOpts, libsql.WithReadYourWrites(l.readYourWrites))
|
||||
}
|
||||
|
||||
connector, err := libsql.NewEmbeddedReplicaConnector(dbPath, primaryUrl,
|
||||
lsOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating connector | %w", err)
|
||||
}
|
||||
|
||||
db := sql.OpenDB(connector)
|
||||
|
||||
err = setupMigrations()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up migrations | %w", err)
|
||||
}
|
||||
|
||||
l.db = db
|
||||
l.connector = connector
|
||||
l.dir = dir
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func libSqlDefaults() *LibSqlDB {
|
||||
return &LibSqlDB{
|
||||
localDBName: "local.db",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *LibSqlDB) Close() error {
|
||||
var resultError *multierror.Error
|
||||
|
||||
if err := t.db.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close database: %w", err))
|
||||
}
|
||||
|
||||
if t.connector != nil {
|
||||
if err := t.connector.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close connector: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
if t.dir != "" {
|
||||
if err := os.RemoveAll(t.dir); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to remove directory %s: %w", t.dir, err))
|
||||
}
|
||||
}
|
||||
|
||||
return resultError.ErrorOrNil()
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//go:build windows
|
||||
|
||||
package libsqlDB
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql"
|
||||
)
|
||||
|
||||
type LibSqlDB struct {
|
||||
db *sql.DB
|
||||
|
||||
dir string // only used for embedded replica
|
||||
localDBName string // only used for embedded replica but needs to be here for consistency
|
||||
authToken string
|
||||
syncInterval time.Duration // only used for embedded replica
|
||||
encryptionKey string // only used for embedded replica
|
||||
readYourWrites *bool // only used for embedded replica
|
||||
}
|
||||
|
||||
func NewLibSqlDB(primaryUrl string, migrationFiles embed.FS, opts ...Options) (*LibSqlDB, error) {
|
||||
l := &LibSqlDB{}
|
||||
|
||||
for _, option := range opts {
|
||||
err := option(l)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error applying options | %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
url := primaryUrl
|
||||
if l.authToken != "" {
|
||||
url = primaryUrl + "?authToken=" + l.authToken
|
||||
}
|
||||
|
||||
db, err := sql.Open("libsql", url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up LibSQL db | %w", err)
|
||||
}
|
||||
|
||||
err = setupMigrations()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up migrations | %w", err)
|
||||
}
|
||||
|
||||
return &LibSqlDB{
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *LibSqlDB) Close() error {
|
||||
var resultError *multierror.Error
|
||||
|
||||
if err := t.db.Close(); err != nil {
|
||||
resultError = multierror.Append(resultError, fmt.Errorf("failed to close database: %w", err))
|
||||
}
|
||||
|
||||
return resultError.ErrorOrNil()
|
||||
}
|
7
main.go
7
main.go
|
@ -3,14 +3,17 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"{{.ModulePath}}/db"
|
||||
)
|
||||
|
||||
func main() {
|
||||
primaryUrl := os.Getenv("LIBSQL_DATABASE_URL")
|
||||
authToken := os.Getenv("LIBSQL_AUTH_TOKEN")
|
||||
|
||||
tdb, err := db.NewLibSqlDB(primaryUrl, authToken, "local.db")
|
||||
tdb, err := libsqlDB.NewLibSqlDB(
|
||||
primaryUrl,
|
||||
libsqlDB.WithAuthToken(authToken),
|
||||
libsqlDB.WithLocalDBName("local.db"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to open db %s: %s", primaryUrl, err)
|
||||
os.Exit(1)
|
||||
|
|
Loading…
Reference in New Issue