Add different data providers and improve docs

This adds support for switching between in-memory, local filesystem, s3
or sqlite.
This commit is contained in:
patwie
2024-03-29 10:02:31 +00:00
parent 2dd1421b6e
commit 31c8029402
10 changed files with 426 additions and 89 deletions
+71
View File
@@ -0,0 +1,71 @@
package aws
import (
"bytes"
"context"
"excalidraw-complete/core"
"fmt"
"io/ioutil"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/oklog/ulid/v2"
)
type documentStore struct {
s3Client *s3.Client
bucket string // Name of the S3 bucket
}
func NewDocumentStore(bucketName string) core.DocumentStore {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Fatalf("unable to load SDK config, %v", err)
}
s3Client := s3.NewFromConfig(cfg)
return &documentStore{
s3Client: s3Client,
bucket: bucketName,
}
}
func (s *documentStore) FindID(ctx context.Context, id string) (*core.Document, error) {
resp, err := s.s3Client.GetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(id),
})
if err != nil {
return nil, fmt.Errorf("failed to get document with id %s: %v", id, err)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read document data: %v", err)
}
document := core.Document{
Data: *bytes.NewBuffer(data),
}
return &document, nil
}
func (s *documentStore) Create(ctx context.Context, document *core.Document) (string, error) {
id := ulid.Make().String()
_, err := s.s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(id),
Body: bytes.NewReader(document.Data.Bytes()),
})
if err != nil {
return "", fmt.Errorf("failed to upload document: %v", err)
}
return id, nil
}
+54
View File
@@ -0,0 +1,54 @@
package filesystem
import (
"bytes"
"context"
"excalidraw-complete/core"
"fmt"
"log"
"os"
"path/filepath"
"github.com/oklog/ulid/v2"
)
type documentStore struct {
basePath string // Directory where documents are stored.
}
func NewDocumentStore(basePath string) core.DocumentStore {
if err := os.MkdirAll(basePath, 0755); err != nil {
log.Fatalf("failed to create base directory: %v", err)
}
return &documentStore{basePath: basePath}
}
func (s *documentStore) FindID(ctx context.Context, id string) (*core.Document, error) {
filePath := filepath.Join(s.basePath, id)
data, err := os.ReadFile(filePath)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("document with id %s not found", id)
}
return nil, err
}
document := core.Document{
Data: *bytes.NewBuffer(data),
}
return &document, nil
}
func (s *documentStore) Create(ctx context.Context, document *core.Document) (string, error) {
id := ulid.Make().String()
filePath := filepath.Join(s.basePath, id)
if err := os.WriteFile(filePath, document.Data.Bytes(), 0644); err != nil {
return "", err
}
return id, nil
}
+31
View File
@@ -0,0 +1,31 @@
package memory
import (
"context"
"excalidraw-complete/core"
"fmt"
"github.com/oklog/ulid/v2"
)
var savedDocuments = make(map[string]core.Document)
type documentStore struct {
}
func NewDocumentStore() core.DocumentStore {
return &documentStore{}
}
func (s *documentStore) FindID(ctx context.Context, id string) (*core.Document, error) {
if val, ok := savedDocuments[id]; ok {
return &val, nil
}
return nil, fmt.Errorf("document with id %s not found", id)
}
func (s *documentStore) Create(ctx context.Context, document *core.Document) (string, error) {
id := ulid.Make().String()
savedDocuments[id] = *document
return id, nil
}
+60
View File
@@ -0,0 +1,60 @@
package sqlite
import (
"bytes"
"context"
"excalidraw-complete/core"
"fmt"
"database/sql"
"log"
_ "github.com/mattn/go-sqlite3"
"github.com/oklog/ulid/v2"
)
var savedDocuments = make(map[string]core.Document)
type documentStore struct {
db *sql.DB
}
func NewDocumentStore(dataSourceName string) core.DocumentStore {
// db, err := sql.Open("sqlite3", ":memory:")
db, err := sql.Open("sqlite3", dataSourceName)
if err != nil {
log.Fatal(err)
}
sts := `CREATE TABLE IF NOT EXISTS documents (id TEXT PRIMARY KEY, data BLOB);`
_, err = db.Exec(sts)
if err != nil {
log.Fatal(err)
}
return &documentStore{db}
}
func (s *documentStore) FindID(ctx context.Context, id string) (*core.Document, error) {
var data []byte
err := s.db.QueryRowContext(ctx, "SELECT data FROM documents WHERE id = ?", id).Scan(&data)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("document with id %s not found", id)
}
return nil, err
}
document := core.Document{
Data: *bytes.NewBuffer(data),
}
return &document, nil
}
func (s *documentStore) Create(ctx context.Context, document *core.Document) (string, error) {
id := ulid.Make().String()
data := document.Data.Bytes()
_, err := s.db.ExecContext(ctx, "INSERT INTO documents (id, data) VALUES (?, ?)", id, data)
if err != nil {
return "", err
}
return id, nil
}
+30
View File
@@ -0,0 +1,30 @@
package stores
import (
"excalidraw-complete/core"
"excalidraw-complete/stores/aws"
"excalidraw-complete/stores/filesystem"
"excalidraw-complete/stores/memory"
"excalidraw-complete/stores/sqlite"
"os"
)
func GetStore() core.DocumentStore {
storageType := os.Getenv("STORAGE_TYPE")
var store core.DocumentStore
switch storageType {
case "filesystem":
basePath := os.Getenv("LOCAL_STORAGE_PATH")
store = filesystem.NewDocumentStore(basePath)
case "sqlite":
dataSourceName := os.Getenv("DATA_SOURCE_NAME")
store = sqlite.NewDocumentStore(dataSourceName)
case "s3":
bucketName := os.Getenv("S3_BUCKET_NAME")
store = aws.NewDocumentStore(bucketName)
default:
store = memory.NewDocumentStore()
}
return store
}