From c4f504773ffd5ae60c2a826b5ed6a34b5bdc3a74 Mon Sep 17 00:00:00 2001 From: Prad N Date: Thu, 29 May 2025 16:23:22 -0400 Subject: [PATCH] feat: add KV store middleware for session and handle management --- cmd/radar/main.go | 3 +- cmd/worker/main.go | 3 +- middleware/database/bindings.go | 31 +++++++++++++++++++ middleware/database/connection.go | 8 +++-- middleware/database/middleware.go | 2 +- middleware/kvstore/bindings.go | 32 ++++++++++++++++++++ middleware/kvstore/connection.go | 46 +++++++++++++++++++++++++++++ middleware/kvstore/middleware.go | 44 +++++++++++++++++++++++++++ middleware/kvstore/store.go | 49 +++++++++++++++++++++++++++++++ 9 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 middleware/kvstore/bindings.go create mode 100644 middleware/kvstore/connection.go create mode 100644 middleware/kvstore/middleware.go create mode 100644 middleware/kvstore/store.go diff --git a/cmd/radar/main.go b/cmd/radar/main.go index 98f1a11..5598e1f 100644 --- a/cmd/radar/main.go +++ b/cmd/radar/main.go @@ -10,6 +10,7 @@ import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/middleware/database" + "github.com/sonr-io/motr/middleware/kvstore" "github.com/sonr-io/motr/middleware/marketapi" "github.com/sonr-io/motr/middleware/session" "github.com/sonr-io/motr/routes" @@ -26,7 +27,7 @@ import ( // Setup the HTTP handler func loadHandler() http.Handler { e := echo.New() - e.Use(session.Middleware(), database.Middleware(), marketapi.Middleware()) + e.Use(session.Middleware(), database.Middleware(), kvstore.Middleware(), marketapi.Middleware()) routes.SetupViews(e) routes.SetupPartials(e) return e diff --git a/cmd/worker/main.go b/cmd/worker/main.go index a7656c7..b0d594a 100644 --- a/cmd/worker/main.go +++ b/cmd/worker/main.go @@ -10,6 +10,7 @@ import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/middleware/database" + "github.com/sonr-io/motr/middleware/kvstore" "github.com/sonr-io/motr/middleware/session" "github.com/sonr-io/motr/middleware/sonrapi" "github.com/sonr-io/motr/middleware/webauthn" @@ -27,7 +28,7 @@ import ( // Setup the HTTP handler func loadHandler() http.Handler { e := echo.New() - e.Use(session.Middleware(), database.Middleware(), sonrapi.Middleware(), webauthn.Middleware()) + e.Use(session.Middleware(), database.Middleware(), kvstore.Middleware(), sonrapi.Middleware(), webauthn.Middleware()) routes.SetupViews(e) routes.SetupPartials(e) return e diff --git a/middleware/database/bindings.go b/middleware/database/bindings.go index 55be09c..4916acf 100644 --- a/middleware/database/bindings.go +++ b/middleware/database/bindings.go @@ -14,11 +14,21 @@ import ( ) var ( + activityDB activity.Querier + networkDB network.Querier + userDB users.Querier +) + +const ( kActivityBinding = "ACTIVITY_DB" kNetworkBinding = "NETWORK_DB" kUsersBinding = "USERS_DB" ) +func IsReady() bool { + return activityDB != nil && networkDB != nil && userDB != nil +} + func (c *connection) initialize() { c.activity = activity.New(openD1(kActivityBinding)) c.network = network.New(openD1(kNetworkBinding)) @@ -33,3 +43,24 @@ func openD1(name string) *sql.DB { } return db } + +func Activity() activity.Querier { + if activityDB == nil { + panic("activityDB is not initialized") + } + return activityDB +} + +func Network() network.Querier { + if networkDB == nil { + panic("networkDB is not initialized") + } + return networkDB +} + +func Users() users.Querier { + if userDB == nil { + panic("userDB is not initialized") + } + return userDB +} diff --git a/middleware/database/connection.go b/middleware/database/connection.go index 9a47240..2f2ac04 100644 --- a/middleware/database/connection.go +++ b/middleware/database/connection.go @@ -9,6 +9,7 @@ import ( "github.com/sonr-io/motr/internal/db/users" ) + // connection is a database context type Connection interface { IsReady() bool @@ -40,11 +41,14 @@ func (q *connection) Users() users.Querier { return q.users } -// Open creates a new database connection -func Open() Connection { +// open creates a new database connection +func open() Connection { conn := &connection{ ready: false, } conn.initialize() + activityDB = conn.Activity() + networkDB = conn.Network() + userDB = conn.Users() return conn } diff --git a/middleware/database/middleware.go b/middleware/database/middleware.go index ef6aa6f..27462f7 100644 --- a/middleware/database/middleware.go +++ b/middleware/database/middleware.go @@ -22,7 +22,7 @@ type context struct { // Middleware is a middleware that adds a new key to the context func Middleware() echo.MiddlewareFunc { - conn := Open() + conn := open() return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { ctx := &context{ diff --git a/middleware/kvstore/bindings.go b/middleware/kvstore/bindings.go new file mode 100644 index 0000000..bea1d75 --- /dev/null +++ b/middleware/kvstore/bindings.go @@ -0,0 +1,32 @@ +//go:build js && wasm +// +build js,wasm + +package kvstore + +var ( + sessionsKV Store + handlesKV Store +) + +const ( + kHandlesBinding = "HANDLES_KV" + kSessionsBinding = "SESSIONS_KV" +) + +func IsReady() bool { + return sessionsKV != nil && handlesKV != nil +} + +func Sessions() Store { + if sessionsKV == nil { + panic("sessionsKV is not initialized") + } + return sessionsKV +} + +func Handles() Store { + if handlesKV == nil { + panic("handlesKV is not initialized") + } + return handlesKV +} diff --git a/middleware/kvstore/connection.go b/middleware/kvstore/connection.go new file mode 100644 index 0000000..0703a1e --- /dev/null +++ b/middleware/kvstore/connection.go @@ -0,0 +1,46 @@ +//go:build js && wasm +// +build js,wasm + +package kvstore + +// connection is a database context +type Connection interface { + Handles() Store + Sessions() Store + IsReady() bool +} + +type connection struct { + ready bool + handles Store + sessions Store +} + +func (q *connection) initialize() { + q.handles = openStore(kHandlesBinding) + q.sessions = openStore(kSessionsBinding) + q.ready = true +} + +func (q *connection) IsReady() bool { + return q.ready +} + +func (q *connection) Handles() Store { + return q.handles +} + +func (q *connection) Sessions() Store { + return q.sessions +} + +// open creates a new database connection +func open() Connection { + conn := &connection{ + ready: false, + } + conn.initialize() + handlesKV = conn.Handles() + sessionsKV = conn.Sessions() + return conn +} diff --git a/middleware/kvstore/middleware.go b/middleware/kvstore/middleware.go new file mode 100644 index 0000000..7df3e53 --- /dev/null +++ b/middleware/kvstore/middleware.go @@ -0,0 +1,44 @@ +//go:build js && wasm +// +build js,wasm + +package kvstore + +import ( + "errors" + + "github.com/labstack/echo/v4" +) + +type Context interface { + echo.Context + Connection +} + +// context is a database context +type context struct { + echo.Context + Connection +} + +// Middleware is a middleware that adds a new key to the context +func Middleware() echo.MiddlewareFunc { + conn := open() + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ctx := &context{ + Context: c, + Connection: conn, + } + return next(ctx) + } + } +} + +// Unwrap unwraps the session context +func Unwrap(c echo.Context) (Context, error) { + cc := c.(*context) + if cc == nil { + return nil, errors.New("failed to unwrap session context") + } + return cc, nil +} diff --git a/middleware/kvstore/store.go b/middleware/kvstore/store.go new file mode 100644 index 0000000..aa79c7a --- /dev/null +++ b/middleware/kvstore/store.go @@ -0,0 +1,49 @@ +//go:build js && wasm +// +build js,wasm + +package kvstore + +import "github.com/syumai/workers/cloudflare/kv" + +type Store interface { + Get(key string) (string, error) + Exists(key string) bool + Set(key string, value string) error + Delete(key string) error + Namespace() *kv.Namespace +} + +func openStore(name string) Store { + k, err := kv.NewNamespace(name) + if err != nil { + panic(err) + } + return &store{ + namespace: k, + } +} + +type store struct { + namespace *kv.Namespace +} + +func (s *store) Namespace() *kv.Namespace { + return s.namespace +} + +func (s *store) Get(key string) (string, error) { + return s.namespace.GetString(key, nil) +} + +func (s *store) Exists(key string) bool { + _, err := s.namespace.GetString(key, nil) + return err == nil +} + +func (s *store) Set(key string, value string) error { + return s.namespace.PutString(key, value, nil) +} + +func (s *store) Delete(key string) error { + return s.namespace.Delete(key) +}