mirror of
https://github.com/alexgo-io/gaze-indexer.git
synced 2026-04-30 04:35:13 +08:00
refactor: Separated modules initiator (#17)
* refactor: separated modules initiator * fix: able to run with empty modules * doc: update cmd desc * refactor: sorting code flow * fix: invalid apionly flow * refactor: remove unnecessary flags * feat: add default value for runes config * refactor: use config instead bind flag to opts struct * chore: remove unused comment * refactor(runes): invoke only when in case * feat: add http server default port * feat: add logger context * doc: update readme --------- Co-authored-by: Gaze <gazenw@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<!-- omit from toc -->
|
||||
|
||||
# Gaze Indexer
|
||||
|
||||
Gaze Indexer is an open-source and modular indexing client for Bitcoin meta-protocols. It has support for Runes out of the box, with **Unified Consistent APIs** across fungible token protocols.
|
||||
@@ -146,11 +147,11 @@ go build -o gaze main.go
|
||||
5. Start the indexer with the `run` command and module flags.
|
||||
|
||||
```bash
|
||||
./gaze run --runes
|
||||
./gaze run --modules runes
|
||||
```
|
||||
|
||||
If `config.yaml` is not located at `./app/config.yaml`, use the `--config` flag to specify the path to the `config.yaml` file.
|
||||
|
||||
```bash
|
||||
./gaze run --runes --config /path/to/config.yaml
|
||||
./gaze run --modules runes --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
275
cmd/cmd_run.go
275
cmd/cmd_run.go
@@ -15,18 +15,10 @@ import (
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"github.com/gaze-network/indexer-network/core/datasources"
|
||||
"github.com/gaze-network/indexer-network/core/indexer"
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
"github.com/gaze-network/indexer-network/internal/config"
|
||||
"github.com/gaze-network/indexer-network/internal/postgres"
|
||||
"github.com/gaze-network/indexer-network/modules/runes"
|
||||
runesapi "github.com/gaze-network/indexer-network/modules/runes/api"
|
||||
runesdatagateway "github.com/gaze-network/indexer-network/modules/runes/datagateway"
|
||||
runespostgres "github.com/gaze-network/indexer-network/modules/runes/repository/postgres"
|
||||
runesusecase "github.com/gaze-network/indexer-network/modules/runes/usecase"
|
||||
"github.com/gaze-network/indexer-network/pkg/automaxprocs"
|
||||
"github.com/gaze-network/indexer-network/pkg/btcclient"
|
||||
"github.com/gaze-network/indexer-network/pkg/errorhandler"
|
||||
"github.com/gaze-network/indexer-network/pkg/logger"
|
||||
"github.com/gaze-network/indexer-network/pkg/logger/slogx"
|
||||
@@ -34,22 +26,17 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
fiberrecover "github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
shutdownTimeout = 60 * time.Second
|
||||
// Register Modules
|
||||
var Modules = do.Package(
|
||||
do.LazyNamed("runes", runes.New),
|
||||
)
|
||||
|
||||
type runCmdOptions struct {
|
||||
APIOnly bool
|
||||
Runes bool
|
||||
}
|
||||
|
||||
func NewRunCommand() *cobra.Command {
|
||||
opts := &runCmdOptions{}
|
||||
|
||||
// Create command
|
||||
runCmd := &cobra.Command{
|
||||
Use: "run",
|
||||
@@ -58,34 +45,30 @@ func NewRunCommand() *cobra.Command {
|
||||
if err := automaxprocs.Init(); err != nil {
|
||||
logger.Error("Failed to set GOMAXPROCS", slogx.Error(err))
|
||||
}
|
||||
return runHandler(opts, cmd, args)
|
||||
return runHandler(cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: separate flags and bind flags to each module cmd package.
|
||||
|
||||
// Add local flags
|
||||
flags := runCmd.Flags()
|
||||
flags.BoolVar(&opts.APIOnly, "api-only", false, "Run only API server")
|
||||
flags.BoolVar(&opts.Runes, "runes", false, "Enable Runes indexer module")
|
||||
flags.String("runes-db", "postgres", `Database to store runes data. current supported databases: "postgres"`)
|
||||
flags.String("runes-datasource", "bitcoin-node", `Datasource to fetch bitcoin data for processing Meta-Protocol data. current supported datasources: "bitcoin-node"`)
|
||||
flags.Bool("api-only", false, "Run only API server")
|
||||
flags.String("modules", "", "Enable specific modules to run. E.g. `runes,brc20`")
|
||||
|
||||
// Bind flags to configuration
|
||||
config.BindPFlag("modules.runes.database", flags.Lookup("runes-db"))
|
||||
config.BindPFlag("modules.runes.datasource", flags.Lookup("runes-datasource"))
|
||||
config.BindPFlag("api_only", flags.Lookup("api-only"))
|
||||
config.BindPFlag("enable_modules", flags.Lookup("modules"))
|
||||
|
||||
return runCmd
|
||||
}
|
||||
|
||||
type HttpHandler interface {
|
||||
Mount(router fiber.Router) error
|
||||
}
|
||||
const (
|
||||
shutdownTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
|
||||
func runHandler(cmd *cobra.Command, _ []string) error {
|
||||
conf := config.Load()
|
||||
|
||||
// Validate inputs
|
||||
// Validate inputs and configurations
|
||||
{
|
||||
if !conf.Network.IsSupported() {
|
||||
return errors.Wrapf(errs.Unsupported, "%q network is not supported", conf.Network.String())
|
||||
@@ -96,131 +79,57 @@ func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
|
||||
ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
// Initialize worker context to separate worker's lifecycle from main process
|
||||
ctxWorker, stopWorker := context.WithCancel(context.Background())
|
||||
defer stopWorker()
|
||||
injector := do.New(Modules)
|
||||
do.ProvideValue(injector, conf)
|
||||
do.ProvideValue(injector, ctx)
|
||||
|
||||
// Add logger context
|
||||
ctxWorker = logger.WithContext(ctxWorker, slogx.Stringer("network", conf.Network))
|
||||
// Initialize Bitcoin RPC client
|
||||
do.Provide(injector, func(i do.Injector) (*rpcclient.Client, error) {
|
||||
conf := do.MustInvoke[config.Config](i)
|
||||
|
||||
// Initialize Bitcoin Core RPC Client
|
||||
client, err := rpcclient.New(&rpcclient.ConnConfig{
|
||||
Host: conf.BitcoinNode.Host,
|
||||
User: conf.BitcoinNode.User,
|
||||
Pass: conf.BitcoinNode.Pass,
|
||||
DisableTLS: conf.BitcoinNode.DisableTLS,
|
||||
HTTPPostMode: true,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
logger.PanicContext(ctx, "Invalid Bitcoin node configuration", slogx.Error(err))
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
// Check Bitcoin RPC connection
|
||||
{
|
||||
start := time.Now()
|
||||
logger.InfoContext(ctx, "Connecting to Bitcoin Core RPC Server...", slogx.String("host", conf.BitcoinNode.Host))
|
||||
if err := client.Ping(); err != nil {
|
||||
logger.PanicContext(ctx, "Can't connect to Bitcoin Core RPC Server", slogx.String("host", conf.BitcoinNode.Host), slogx.Error(err))
|
||||
client, err := rpcclient.New(&rpcclient.ConnConfig{
|
||||
Host: conf.BitcoinNode.Host,
|
||||
User: conf.BitcoinNode.User,
|
||||
Pass: conf.BitcoinNode.Pass,
|
||||
DisableTLS: conf.BitcoinNode.DisableTLS,
|
||||
HTTPPostMode: true,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid Bitcoin node configuration")
|
||||
}
|
||||
logger.InfoContext(ctx, "Connected to Bitcoin Core RPC Server", slog.Duration("latency", time.Since(start)))
|
||||
}
|
||||
|
||||
// TODO: create module command package.
|
||||
// each module should have its own command package and main package will routing the command to the module command package.
|
||||
// Check Bitcoin RPC connection
|
||||
{
|
||||
start := time.Now()
|
||||
logger.InfoContext(ctx, "Connecting to Bitcoin Core RPC Server...", slogx.String("host", conf.BitcoinNode.Host))
|
||||
if err := client.Ping(); err != nil {
|
||||
return nil, errors.Wrapf(err, "can't connect to Bitcoin Core RPC Server %q", conf.BitcoinNode.Host)
|
||||
}
|
||||
logger.InfoContext(ctx, "Connected to Bitcoin Core RPC Server", slog.Duration("latency", time.Since(start)))
|
||||
}
|
||||
|
||||
// TODO: refactor module name to specific type instead of string?
|
||||
httpHandlers := make(map[string]HttpHandler, 0)
|
||||
return client, nil
|
||||
})
|
||||
|
||||
var reportingClient *reportingclient.ReportingClient
|
||||
if !conf.Reporting.Disabled {
|
||||
reportingClient, err = reportingclient.New(conf.Reporting)
|
||||
// Initialize reporting client
|
||||
do.Provide(injector, func(i do.Injector) (*reportingclient.ReportingClient, error) {
|
||||
conf := do.MustInvoke[config.Config](i)
|
||||
if conf.Reporting.Disabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
reportingClient, err := reportingclient.New(conf.Reporting)
|
||||
if err != nil {
|
||||
if errors.Is(err, errs.InvalidArgument) {
|
||||
logger.PanicContext(ctx, "Invalid reporting configuration", slogx.Error(err))
|
||||
return nil, errors.Wrap(err, "invalid reporting configuration")
|
||||
}
|
||||
logger.PanicContext(ctx, "Something went wrong, can't create reporting client", slogx.Error(err))
|
||||
return nil, errors.Wrap(err, "can't create reporting client")
|
||||
}
|
||||
}
|
||||
return reportingClient, nil
|
||||
})
|
||||
|
||||
// Initialize Runes Indexer
|
||||
if opts.Runes {
|
||||
ctx := logger.WithContext(ctx, slogx.String("module", "runes"))
|
||||
var (
|
||||
runesDg runesdatagateway.RunesDataGateway
|
||||
indexerInfoDg runesdatagateway.IndexerInfoDataGateway
|
||||
)
|
||||
switch strings.ToLower(conf.Modules.Runes.Database) {
|
||||
case "postgresql", "postgres", "pg":
|
||||
pg, err := postgres.NewPool(ctx, conf.Modules.Runes.Postgres)
|
||||
if err != nil {
|
||||
if errors.Is(err, errs.InvalidArgument) {
|
||||
logger.PanicContext(ctx, "Invalid Postgres configuration for indexer", slogx.Error(err))
|
||||
}
|
||||
logger.PanicContext(ctx, "Something went wrong, can't create Postgres connection pool", slogx.Error(err))
|
||||
}
|
||||
defer pg.Close()
|
||||
runesRepo := runespostgres.NewRepository(pg)
|
||||
runesDg = runesRepo
|
||||
indexerInfoDg = runesRepo
|
||||
default:
|
||||
return errors.Wrapf(errs.Unsupported, "%q database for indexer is not supported", conf.Modules.Runes.Database)
|
||||
}
|
||||
var bitcoinDatasource datasources.Datasource[*types.Block]
|
||||
var bitcoinClient btcclient.Contract
|
||||
switch strings.ToLower(conf.Modules.Runes.Datasource) {
|
||||
case "bitcoin-node":
|
||||
bitcoinNodeDatasource := datasources.NewBitcoinNode(client)
|
||||
bitcoinDatasource = bitcoinNodeDatasource
|
||||
bitcoinClient = bitcoinNodeDatasource
|
||||
default:
|
||||
return errors.Wrapf(errs.Unsupported, "%q datasource is not supported", conf.Modules.Runes.Datasource)
|
||||
}
|
||||
|
||||
if !opts.APIOnly {
|
||||
processor := runes.NewProcessor(runesDg, indexerInfoDg, bitcoinClient, conf.Network, reportingClient)
|
||||
indexer := indexer.New(processor, bitcoinDatasource)
|
||||
defer func() {
|
||||
if err := indexer.ShutdownWithTimeout(shutdownTimeout); err != nil {
|
||||
logger.ErrorContext(ctx, "Error during shutdown indexer", slogx.Error(err))
|
||||
return
|
||||
}
|
||||
logger.InfoContext(ctx, "Indexer stopped gracefully")
|
||||
}()
|
||||
|
||||
if err := processor.VerifyStates(ctx); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Run Indexer
|
||||
go func() {
|
||||
// stop main process if indexer stopped
|
||||
defer stop()
|
||||
|
||||
logger.InfoContext(ctx, "Starting Gaze Indexer")
|
||||
if err := indexer.Run(ctxWorker); err != nil {
|
||||
logger.PanicContext(ctx, "Something went wrong, error during running indexer", slogx.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Mount API
|
||||
apiHandlers := lo.Uniq(conf.Modules.Runes.APIHandlers)
|
||||
for _, handler := range apiHandlers {
|
||||
switch handler { // TODO: support more handlers (e.g. gRPC)
|
||||
case "http":
|
||||
runesUsecase := runesusecase.New(runesDg, bitcoinClient)
|
||||
runesHTTPHandler := runesapi.NewHTTPHandler(conf.Network, runesUsecase)
|
||||
httpHandlers["runes"] = runesHTTPHandler
|
||||
default:
|
||||
logger.PanicContext(ctx, "Something went wrong, unsupported API handler", slogx.String("handler", handler))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for interrupt signal to gracefully stop the server with
|
||||
// Setup HTTP server if there are any HTTP handlers
|
||||
if len(httpHandlers) > 0 {
|
||||
// Initialize HTTP server
|
||||
do.Provide(injector, func(i do.Injector) (*fiber.App, error) {
|
||||
app := fiber.New(fiber.Config{
|
||||
AppName: "Gaze Indexer",
|
||||
ErrorHandler: errorhandler.NewHTTPErrorHandler(),
|
||||
@@ -238,38 +147,64 @@ func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
|
||||
Level: compress.LevelDefault,
|
||||
}))
|
||||
|
||||
defer func() {
|
||||
if err := app.ShutdownWithTimeout(shutdownTimeout); err != nil {
|
||||
logger.ErrorContext(ctx, "Error during shutdown HTTP server", slogx.Error(err))
|
||||
return
|
||||
}
|
||||
logger.InfoContext(ctx, "HTTP server stopped gracefully")
|
||||
}()
|
||||
|
||||
// Health check
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return errors.WithStack(c.SendStatus(http.StatusOK))
|
||||
})
|
||||
|
||||
// mount http handlers from each http-enabled module
|
||||
for module, handler := range httpHandlers {
|
||||
if err := handler.Mount(app); err != nil {
|
||||
logger.PanicContext(ctx, "Something went wrong, can't mount HTTP handler", slogx.Error(err), slogx.String("module", module))
|
||||
return app, nil
|
||||
})
|
||||
|
||||
// Initialize worker context to separate worker's lifecycle from main process
|
||||
ctxWorker, stopWorker := context.WithCancel(context.Background())
|
||||
defer stopWorker()
|
||||
|
||||
// Add logger context
|
||||
ctxWorker = logger.WithContext(ctxWorker, slogx.Stringer("network", conf.Network))
|
||||
|
||||
// Run modules
|
||||
{
|
||||
modules := lo.Uniq(conf.EnableModules)
|
||||
modules = lo.Map(modules, func(item string, _ int) string { return strings.TrimSpace(item) })
|
||||
modules = lo.Filter(modules, func(item string, _ int) bool { return item != "" })
|
||||
for _, module := range modules {
|
||||
ctx := logger.WithContext(ctxWorker, slogx.String("module", module))
|
||||
|
||||
indexer, err := do.InvokeNamed[indexer.IndexerWorker](injector, module)
|
||||
if err != nil {
|
||||
if errors.Is(err, do.ErrServiceNotFound) {
|
||||
return errors.Errorf("Module %q is not supported", module)
|
||||
}
|
||||
return errors.Wrapf(err, "can't init module %q", module)
|
||||
}
|
||||
|
||||
// Run Indexer
|
||||
if !conf.APIOnly {
|
||||
go func() {
|
||||
// stop main process if indexer stopped
|
||||
defer stop()
|
||||
|
||||
logger.InfoContext(ctx, "Starting Gaze Indexer")
|
||||
if err := indexer.Run(ctx); err != nil {
|
||||
logger.PanicContext(ctx, "Something went wrong, error during running indexer", slogx.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
logger.InfoContext(ctx, "Mounted HTTP handler", slogx.String("module", module))
|
||||
}
|
||||
|
||||
go func() {
|
||||
// stop main process if API stopped
|
||||
defer stop()
|
||||
|
||||
logger.InfoContext(ctx, "Started HTTP server", slog.Int("port", conf.HTTPServer.Port))
|
||||
if err := app.Listen(fmt.Sprintf(":%d", conf.HTTPServer.Port)); err != nil {
|
||||
logger.PanicContext(ctx, "Something went wrong, error during running HTTP server", slogx.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Run API server
|
||||
httpServer := do.MustInvoke[*fiber.App](injector)
|
||||
go func() {
|
||||
// stop main process if API stopped
|
||||
defer stop()
|
||||
|
||||
logger.InfoContext(ctx, "Started HTTP server", slog.Int("port", conf.HTTPServer.Port))
|
||||
if err := httpServer.Listen(fmt.Sprintf(":%d", conf.HTTPServer.Port)); err != nil {
|
||||
logger.PanicContext(ctx, "Something went wrong, error during running HTTP server", slogx.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
// Stop application if worker context is done
|
||||
go func() {
|
||||
<-ctxWorker.Done()
|
||||
@@ -298,5 +233,9 @@ func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := injector.Shutdown(); err != nil {
|
||||
logger.PanicContext(ctx, "Failed while gracefully shutting down", slogx.Error(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package indexer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
)
|
||||
@@ -29,3 +30,10 @@ type Processor[T Input] interface {
|
||||
// to ensure the last shutdown was graceful and no missing data.
|
||||
VerifyStates(ctx context.Context) error
|
||||
}
|
||||
|
||||
type IndexerWorker interface {
|
||||
Shutdown() error
|
||||
ShutdownWithTimeout(timeout time.Duration) error
|
||||
ShutdownWithContext(ctx context.Context) (err error)
|
||||
Run(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -14,6 +14,7 @@ require (
|
||||
github.com/jackc/pgx/v5 v5.5.5
|
||||
github.com/mcosta74/pgx-slog v0.3.0
|
||||
github.com/planxnx/concurrent-stream v0.1.5
|
||||
github.com/samber/do/v2 v2.0.0-beta.7
|
||||
github.com/samber/lo v1.39.0
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/spf13/cobra v1.8.0
|
||||
@@ -64,6 +65,7 @@ require (
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/samber/go-type-to-string v1.4.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -191,6 +191,10 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/samber/do/v2 v2.0.0-beta.7 h1:tmdLOVSCbTA6uGWLU5poi/nZvMRh5QxXFJ9vHytU+Jk=
|
||||
github.com/samber/do/v2 v2.0.0-beta.7/go.mod h1:+LpV3vu4L81Q1JMZNSkMvSkW9lt4e5eJoXoZHkeBS4c=
|
||||
github.com/samber/go-type-to-string v1.4.0 h1:KXphToZgiFdnJQxryU25brhlh/CqY/cwJVeX2rfmow0=
|
||||
github.com/samber/go-type-to-string v1.4.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU=
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
@@ -231,6 +235,8 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
||||
@@ -24,20 +24,31 @@ var (
|
||||
Output: "TEXT",
|
||||
},
|
||||
Network: common.NetworkMainnet,
|
||||
HTTPServer: HTTPServerConfig{
|
||||
Port: 8080,
|
||||
},
|
||||
BitcoinNode: BitcoinNodeClient{
|
||||
User: "user",
|
||||
Pass: "pass",
|
||||
},
|
||||
Modules: Modules{
|
||||
Runes: runesconfig.Config{
|
||||
Datasource: "bitcoin-node",
|
||||
Database: "postgres",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Logger logger.Config `mapstructure:"logger"`
|
||||
BitcoinNode BitcoinNodeClient `mapstructure:"bitcoin_node"`
|
||||
Network common.Network `mapstructure:"network"`
|
||||
HTTPServer HTTPServerConfig `mapstructure:"http_server"`
|
||||
Modules Modules `mapstructure:"modules"`
|
||||
Reporting reportingclient.Config `mapstructure:"reporting"`
|
||||
EnableModules []string `mapstructure:"enable_modules"`
|
||||
APIOnly bool `mapstructure:"api_only"`
|
||||
Logger logger.Config `mapstructure:"logger"`
|
||||
BitcoinNode BitcoinNodeClient `mapstructure:"bitcoin_node"`
|
||||
Network common.Network `mapstructure:"network"`
|
||||
HTTPServer HTTPServerConfig `mapstructure:"http_server"`
|
||||
Modules Modules `mapstructure:"modules"`
|
||||
Reporting reportingclient.Config `mapstructure:"reporting"`
|
||||
}
|
||||
|
||||
type BitcoinNodeClient struct {
|
||||
|
||||
89
modules/runes/runes.go
Normal file
89
modules/runes/runes.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package runes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"github.com/gaze-network/indexer-network/core/datasources"
|
||||
"github.com/gaze-network/indexer-network/core/indexer"
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
"github.com/gaze-network/indexer-network/internal/config"
|
||||
"github.com/gaze-network/indexer-network/internal/postgres"
|
||||
runesapi "github.com/gaze-network/indexer-network/modules/runes/api"
|
||||
runesdatagateway "github.com/gaze-network/indexer-network/modules/runes/datagateway"
|
||||
runespostgres "github.com/gaze-network/indexer-network/modules/runes/repository/postgres"
|
||||
runesusecase "github.com/gaze-network/indexer-network/modules/runes/usecase"
|
||||
"github.com/gaze-network/indexer-network/pkg/btcclient"
|
||||
"github.com/gaze-network/indexer-network/pkg/logger"
|
||||
"github.com/gaze-network/indexer-network/pkg/reportingclient"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func New(injector do.Injector) (indexer.IndexerWorker, error) {
|
||||
ctx := do.MustInvoke[context.Context](injector)
|
||||
conf := do.MustInvoke[config.Config](injector)
|
||||
reportingClient := do.MustInvoke[*reportingclient.ReportingClient](injector)
|
||||
|
||||
var (
|
||||
runesDg runesdatagateway.RunesDataGateway
|
||||
indexerInfoDg runesdatagateway.IndexerInfoDataGateway
|
||||
)
|
||||
switch strings.ToLower(conf.Modules.Runes.Database) {
|
||||
case "postgresql", "postgres", "pg":
|
||||
pg, err := postgres.NewPool(ctx, conf.Modules.Runes.Postgres)
|
||||
if err != nil {
|
||||
if errors.Is(err, errs.InvalidArgument) {
|
||||
return nil, errors.Wrap(err, "Invalid Postgres configuration for indexer")
|
||||
}
|
||||
return nil, errors.Wrap(err, "can't create Postgres connection pool")
|
||||
}
|
||||
defer pg.Close()
|
||||
runesRepo := runespostgres.NewRepository(pg)
|
||||
runesDg = runesRepo
|
||||
indexerInfoDg = runesRepo
|
||||
default:
|
||||
return nil, errors.Wrapf(errs.Unsupported, "%q database for indexer is not supported", conf.Modules.Runes.Database)
|
||||
}
|
||||
|
||||
var bitcoinDatasource datasources.Datasource[*types.Block]
|
||||
var bitcoinClient btcclient.Contract
|
||||
switch strings.ToLower(conf.Modules.Runes.Datasource) {
|
||||
case "bitcoin-node":
|
||||
btcClient := do.MustInvoke[*rpcclient.Client](injector)
|
||||
bitcoinNodeDatasource := datasources.NewBitcoinNode(btcClient)
|
||||
bitcoinDatasource = bitcoinNodeDatasource
|
||||
bitcoinClient = bitcoinNodeDatasource
|
||||
default:
|
||||
return nil, errors.Wrapf(errs.Unsupported, "%q datasource is not supported", conf.Modules.Runes.Datasource)
|
||||
}
|
||||
|
||||
processor := NewProcessor(runesDg, indexerInfoDg, bitcoinClient, conf.Network, reportingClient)
|
||||
if err := processor.VerifyStates(ctx); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Mount API
|
||||
apiHandlers := lo.Uniq(conf.Modules.Runes.APIHandlers)
|
||||
for _, handler := range apiHandlers {
|
||||
switch handler { // TODO: support more handlers (e.g. gRPC)
|
||||
case "http":
|
||||
httpServer := do.MustInvoke[*fiber.App](injector)
|
||||
runesUsecase := runesusecase.New(runesDg, bitcoinClient)
|
||||
runesHTTPHandler := runesapi.NewHTTPHandler(conf.Network, runesUsecase)
|
||||
if err := runesHTTPHandler.Mount(httpServer); err != nil {
|
||||
return nil, errors.Wrap(err, "can't mount Runes API")
|
||||
}
|
||||
logger.InfoContext(ctx, "Mounted HTTP handler")
|
||||
default:
|
||||
return nil, errors.Wrapf(errs.Unsupported, "%q API handler is not supported", handler)
|
||||
}
|
||||
}
|
||||
|
||||
indexer := indexer.New(processor, bitcoinDatasource)
|
||||
return indexer, nil
|
||||
}
|
||||
Reference in New Issue
Block a user