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 (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Migrations struct {
|
type Migrations struct {
|
||||||
|
@ -13,8 +14,10 @@ type Migrations struct {
|
||||||
query string
|
query string
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed migrations/*.sql
|
type Options func(*LibSqlDB) error
|
||||||
var migrationFiles embed.FS
|
|
||||||
|
// use something like this in the user's code -> //go:embed migrations/*.sql
|
||||||
|
var _migrationFiles embed.FS
|
||||||
|
|
||||||
var migrations []Migrations
|
var migrations []Migrations
|
||||||
|
|
||||||
|
@ -26,12 +29,12 @@ var migrations []Migrations
|
||||||
// setupMigrations initializes the filesystem and reads the migration files into the migrations variable
|
// setupMigrations initializes the filesystem and reads the migration files into the migrations variable
|
||||||
func setupMigrations() error {
|
func setupMigrations() error {
|
||||||
// Walk through the embedded files and read their contents
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !d.IsDir() {
|
if !d.IsDir() {
|
||||||
content, err := migrationFiles.ReadFile(path)
|
content, err := _migrationFiles.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -87,3 +90,51 @@ func (t *LibSqlDB) Migrate() error {
|
||||||
}
|
}
|
||||||
return nil
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"{{.ModulePath}}/db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
primaryUrl := os.Getenv("LIBSQL_DATABASE_URL")
|
primaryUrl := os.Getenv("LIBSQL_DATABASE_URL")
|
||||||
authToken := os.Getenv("LIBSQL_AUTH_TOKEN")
|
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 {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "failed to open db %s: %s", primaryUrl, err)
|
fmt.Fprintf(os.Stderr, "failed to open db %s: %s", primaryUrl, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
Loading…
Reference in New Issue