diff --git a/.github/setup_env.sh b/.github/setup_env.sh index b4adec0..2eb141e 100644 --- a/.github/setup_env.sh +++ b/.github/setup_env.sh @@ -9,6 +9,8 @@ export PATH=$GOBIN:$PATH export ROOT_DIR=$(git rev-parse --show-toplevel) export RADAR_ROOT=$ROOT_DIR/cmd/radar export WORKER_ROOT=$ROOT_DIR/cmd/worker +export SQLC_ROOT=$ROOT_DIR/internal/db +export MIGRATE_ROOT=$ROOT_DIR/internal/migrate # Setup Build Outputs export RADAR_OUT=$RADAR_ROOT/build/app.wasm diff --git a/Makefile b/Makefile index e6a265f..dfa9f65 100644 --- a/Makefile +++ b/Makefile @@ -33,3 +33,6 @@ release: templ-watch: @devbox run watch:templ + +migrate: + @devbox run db:migrate diff --git a/cmd/radar/main.go b/cmd/radar/main.go index a4c5330..98f1a11 100644 --- a/cmd/radar/main.go +++ b/cmd/radar/main.go @@ -4,19 +4,63 @@ package main import ( + "context" + "fmt" + "net/http" + "github.com/labstack/echo/v4" - "github.com/sonr-io/motr/pkg/database" - "github.com/sonr-io/motr/pkg/session" + "github.com/sonr-io/motr/middleware/database" + "github.com/sonr-io/motr/middleware/marketapi" + "github.com/sonr-io/motr/middleware/session" "github.com/sonr-io/motr/routes" "github.com/syumai/workers" + "github.com/syumai/workers/cloudflare/cron" + _ "github.com/syumai/workers/cloudflare/d1" ) -func main() { - e := echo.New() - e.Use(session.Middleware(), database.Middleware()) +// ╭──────────────────────────────────────────────────╮ +// │ Initialization │ +// ╰──────────────────────────────────────────────────╯ +// Setup the HTTP handler +func loadHandler() http.Handler { + e := echo.New() + e.Use(session.Middleware(), database.Middleware(), marketapi.Middleware()) routes.SetupViews(e) routes.SetupPartials(e) - workers.Serve(e) + return e +} + +// Setup the cron task +func loadTask() cron.Task { + return func(ctx context.Context) error { + e, err := cron.NewEvent(ctx) + if err != nil { + return err + } + fmt.Println(e.ScheduledTime.Unix()) + return nil + } +} + +// ╭─────────────────────────────────────────────────╮ +// │ Main Function │ +// ╰─────────────────────────────────────────────────╯ + +func main() { + // Setup CRON jobs + e := loadHandler() + t := loadTask() + + // Configure Worker + workers.ServeNonBlock(e) + cron.ScheduleTaskNonBlock(t) + workers.Ready() + + // Block until handler/task is done + select { + case <-workers.Done(): + case <-cron.Done(): + } } diff --git a/cmd/radar/wrangler.toml b/cmd/radar/wrangler.toml index 22738b7..2a67419 100644 --- a/cmd/radar/wrangler.toml +++ b/cmd/radar/wrangler.toml @@ -16,17 +16,31 @@ port = 4242 [observability] enabled = true +[triggers] +crons = ["0 */1 * * *"] + + [[d1_databases]] -binding = "DB" # available in your Worker on env.DB -database_name = "motr-controller-db" -database_id = "872a4b08-7e07-4978-b227-5b60940238ed" +binding = "ACTIVITY_DB" +database_name = "motr-activity" +database_id = "a7ccb4bb-c529-4f42-8029-92564a3aecb8" + +[[d1_databases]] +binding = "NETWORK_DB" +database_name = "motr-network" +database_id = "acb75499-3502-4052-9604-263a913e077a" + +[[d1_databases]] +binding = "USERS_DB" +database_name = "motr-users" +database_id = "8ed4d399-5932-419c-b92f-9c20d7a36ad2" [[kv_namespaces]] -binding = "SESSIONS" # available in your Worker on env.KV +binding = "SESSIONS_KV" id = "ea5de66fcfc14b5eba170395e29432ee" [[kv_namespaces]] -binding = "HANDLES" # available in your Worker on env.KV +binding = "HANDLES_KV" id = "271d47087a8842b2aac5ee79cf7bb203" [[r2_buckets]] diff --git a/cmd/worker/main.go b/cmd/worker/main.go index a8372ad..a7656c7 100644 --- a/cmd/worker/main.go +++ b/cmd/worker/main.go @@ -4,20 +4,64 @@ package main import ( + "context" + "fmt" + "net/http" + "github.com/labstack/echo/v4" - "github.com/sonr-io/motr/pkg/database" - "github.com/sonr-io/motr/pkg/session" + "github.com/sonr-io/motr/middleware/database" + "github.com/sonr-io/motr/middleware/session" + "github.com/sonr-io/motr/middleware/sonrapi" + "github.com/sonr-io/motr/middleware/webauthn" "github.com/sonr-io/motr/routes" "github.com/syumai/workers" + "github.com/syumai/workers/cloudflare/cron" + _ "github.com/syumai/workers/cloudflare/d1" ) -func main() { - e := echo.New() - e.Use(session.Middleware(), database.Middleware()) +// ╭──────────────────────────────────────────────────╮ +// │ Initialization │ +// ╰──────────────────────────────────────────────────╯ +// Setup the HTTP handler +func loadHandler() http.Handler { + e := echo.New() + e.Use(session.Middleware(), database.Middleware(), sonrapi.Middleware(), webauthn.Middleware()) routes.SetupViews(e) routes.SetupPartials(e) - - workers.Serve(e) + return e +} + +// Setup the cron task +func loadTask() cron.Task { + return func(ctx context.Context) error { + e, err := cron.NewEvent(ctx) + if err != nil { + return err + } + fmt.Println(e.ScheduledTime.Unix()) + return nil + } +} + +// ╭─────────────────────────────────────────────────╮ +// │ Main Function │ +// ╰─────────────────────────────────────────────────╯ + +func main() { + // Setup CRON jobs + e := loadHandler() + t := loadTask() + + // Configure Worker + workers.ServeNonBlock(e) + cron.ScheduleTaskNonBlock(t) + workers.Ready() + + // Block until handler/task is done + select { + case <-workers.Done(): + case <-cron.Done(): + } } diff --git a/cmd/worker/wrangler.toml b/cmd/worker/wrangler.toml index 5c90ff3..b898e6a 100644 --- a/cmd/worker/wrangler.toml +++ b/cmd/worker/wrangler.toml @@ -16,17 +16,30 @@ port = 6969 [observability] enabled = true +[triggers] +crons = ["0 */1 * * *"] + [[d1_databases]] -binding = "DB" # available in your Worker on env.DB -database_name = "motr-controller-db" -database_id = "872a4b08-7e07-4978-b227-5b60940238ed" +binding = "ACTIVITY_DB" +database_name = "motr-activity" +database_id = "a7ccb4bb-c529-4f42-8029-92564a3aecb8" + +[[d1_databases]] +binding = "NETWORK_DB" +database_name = "motr-network" +database_id = "acb75499-3502-4052-9604-263a913e077a" + +[[d1_databases]] +binding = "USERS_DB" +database_name = "motr-users" +database_id = "8ed4d399-5932-419c-b92f-9c20d7a36ad2" [[kv_namespaces]] -binding = "SESSIONS" # available in your Worker on env.KV +binding = "SESSIONS_KV" id = "ea5de66fcfc14b5eba170395e29432ee" [[kv_namespaces]] -binding = "HANDLES" # available in your Worker on env.KV +binding = "HANDLES_KV" id = "271d47087a8842b2aac5ee79cf7bb203" [[r2_buckets]] diff --git a/devbox.json b/devbox.json index d17f5f2..f4bfdc3 100644 --- a/devbox.json +++ b/devbox.json @@ -5,9 +5,8 @@ "sqlc@latest", "nodejs@latest", "goreleaser@latest", - "doppler@latest", - "commitizen@latest", - "process-compose@latest" + "go-task@latest", + "doppler@latest" ], "shell": { "init_hook": [ @@ -17,9 +16,6 @@ "npm_install $WORKER_ROOT" ], "scripts": { - "dev": [ - "process-compose up" - ], "deploy": [ "wrangler_deploy $RADAR_ROOT", "wrangler_deploy $WORKER_ROOT" @@ -48,7 +44,10 @@ "npm run start" ], "gen:sqlc": [ - "cd ./internal/sink && sqlc generate" + "cd $SQLC_ROOT && sqlc generate" + ], + "db:migrate": [ + "cd $MIGRATE_ROOT && task" ], "gen:templ": [ "templ generate" diff --git a/devbox.lock b/devbox.lock index 8c68b32..8a6c2fb 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,70 +1,6 @@ { "lockfile_version": "1", "packages": { - "commitizen@latest": { - "last_modified": "2025-05-21T01:27:34Z", - "resolved": "github:NixOS/nixpkgs/f0d925b947cca0bbe7f2d25115cbaf021844aba7#commitizen", - "source": "devbox-search", - "version": "4.7.1", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/m3307fx56ifajpg0qgqjj9dg71xjcfn4-python3.12-commitizen-4.7.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/1081mrd0v09sx6g2c12h2bsyyim0s204-python3.12-commitizen-4.7.1-dist" - } - ], - "store_path": "/nix/store/m3307fx56ifajpg0qgqjj9dg71xjcfn4-python3.12-commitizen-4.7.1" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/kiaab8hfkacad3nh3ygc9p6m02sskb0l-python3.12-commitizen-4.7.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/y7yqhxmmfnb5i012xvkzg8g5qkq8vrkj-python3.12-commitizen-4.7.1-dist" - } - ], - "store_path": "/nix/store/kiaab8hfkacad3nh3ygc9p6m02sskb0l-python3.12-commitizen-4.7.1" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/06s8d1qwgd1mw0jmp35fhfdznwpppb0l-python3.12-commitizen-4.7.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/nlvvhrsg465n918474zh3jirlifvqbww-python3.12-commitizen-4.7.1-dist" - } - ], - "store_path": "/nix/store/06s8d1qwgd1mw0jmp35fhfdznwpppb0l-python3.12-commitizen-4.7.1" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/gaqvcwn0dya1crk789s0xsf7s4knraxz-python3.12-commitizen-4.7.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/iagih5hd0nq8a5fh3naads5xc3gpg9jn-python3.12-commitizen-4.7.1-dist" - } - ], - "store_path": "/nix/store/gaqvcwn0dya1crk789s0xsf7s4knraxz-python3.12-commitizen-4.7.1" - } - } - }, "doppler@latest": { "last_modified": "2025-05-16T20:19:48Z", "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#doppler", @@ -117,6 +53,54 @@ "last_modified": "2025-05-26T00:03:27Z", "resolved": "github:NixOS/nixpkgs/3108eaa516ae22c2360928589731a4f1581526ef?lastModified=1748217807&narHash=sha256-P3u2PXxMlo49PutQLnk2PhI%2FimC69hFl1yY4aT5Nax8%3D" }, + "go-task@latest": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#go-task", + "source": "devbox-search", + "version": "3.43.3", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5h0ykqayf8kfxwqlawzxvm05hlq7n22c-go-task-3.43.3", + "default": true + } + ], + "store_path": "/nix/store/5h0ykqayf8kfxwqlawzxvm05hlq7n22c-go-task-3.43.3" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/xxl2rg7k3c95gk5s0kwj8p8055r4yldf-go-task-3.43.3", + "default": true + } + ], + "store_path": "/nix/store/xxl2rg7k3c95gk5s0kwj8p8055r4yldf-go-task-3.43.3" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/4az7i4f6lm4ss77wnf8kklkr21hrfhns-go-task-3.43.3", + "default": true + } + ], + "store_path": "/nix/store/4az7i4f6lm4ss77wnf8kklkr21hrfhns-go-task-3.43.3" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/gjdgw1zhvl9hl7mmiw6ncpnpmpxlk2yb-go-task-3.43.3", + "default": true + } + ], + "store_path": "/nix/store/gjdgw1zhvl9hl7mmiw6ncpnpmpxlk2yb-go-task-3.43.3" + } + } + }, "goreleaser@latest": { "last_modified": "2025-05-16T20:19:48Z", "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#goreleaser", @@ -246,54 +230,6 @@ } } }, - "process-compose@latest": { - "last_modified": "2025-05-16T20:19:48Z", - "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#process-compose", - "source": "devbox-search", - "version": "1.64.1", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/yffykcwzkaqy490wzkiyw0ff6lss5fcx-process-compose-1.64.1", - "default": true - } - ], - "store_path": "/nix/store/yffykcwzkaqy490wzkiyw0ff6lss5fcx-process-compose-1.64.1" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/2n23nlnm52pi84vn3g8mg14xxsg5ww00-process-compose-1.64.1", - "default": true - } - ], - "store_path": "/nix/store/2n23nlnm52pi84vn3g8mg14xxsg5ww00-process-compose-1.64.1" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/g3r6rxrfk09vfv19rzpd6fmc9s75d21p-process-compose-1.64.1", - "default": true - } - ], - "store_path": "/nix/store/g3r6rxrfk09vfv19rzpd6fmc9s75d21p-process-compose-1.64.1" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4hymfhkv9ldhbjqz6wjln05bhyxfd9xl-process-compose-1.64.1", - "default": true - } - ], - "store_path": "/nix/store/4hymfhkv9ldhbjqz6wjln05bhyxfd9xl-process-compose-1.64.1" - } - } - }, "sqlc@latest": { "last_modified": "2025-05-16T20:19:48Z", "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#sqlc", diff --git a/handlers/error_handler.go b/handlers/error_handler.go index 722a5a4..7c8f3f1 100644 --- a/handlers/error_handler.go +++ b/handlers/error_handler.go @@ -3,7 +3,7 @@ package handlers import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/internal/ui/home" - "github.com/sonr-io/motr/pkg/render" + "github.com/sonr-io/motr/middleware/render" ) func HandleItemNotFound(c echo.Context) error { diff --git a/handlers/index_handler.go b/handlers/index_handler.go index c0126a8..cdf2c0c 100644 --- a/handlers/index_handler.go +++ b/handlers/index_handler.go @@ -3,7 +3,7 @@ package handlers import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/internal/ui/home" - "github.com/sonr-io/motr/pkg/render" + "github.com/sonr-io/motr/middleware/render" ) func HandleDefaultIndex(c echo.Context) error { diff --git a/handlers/login_handler.go b/handlers/login_handler.go index 09a090f..a4144f5 100644 --- a/handlers/login_handler.go +++ b/handlers/login_handler.go @@ -3,18 +3,10 @@ package handlers import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/internal/ui/login" - "github.com/sonr-io/motr/pkg/render" - "github.com/sonr-io/motr/pkg/webauth" + "github.com/sonr-io/motr/middleware/render" ) -type LoginOptions struct { - Account string - Handle string - HelpText string - Label string - Challenge string - AllowedCredentials []*webauth.CredentialDescriptor -} + func HandleLoginCheck(c echo.Context) error { return render.View(c, login.LoginView()) diff --git a/handlers/register_handler.go b/handlers/register_handler.go index cf9d2c4..fadfcaa 100644 --- a/handlers/register_handler.go +++ b/handlers/register_handler.go @@ -3,7 +3,7 @@ package handlers import ( "github.com/labstack/echo/v4" "github.com/sonr-io/motr/internal/ui/register" - "github.com/sonr-io/motr/pkg/render" + "github.com/sonr-io/motr/middleware/render" ) type RegisterOptions struct { diff --git a/internal/sink/activity/db.go b/internal/db/activity/db.go similarity index 100% rename from internal/sink/activity/db.go rename to internal/db/activity/db.go diff --git a/internal/sink/activity/models.go b/internal/db/activity/models.go similarity index 58% rename from internal/sink/activity/models.go rename to internal/db/activity/models.go index df36507..ad7d694 100644 --- a/internal/sink/activity/models.go +++ b/internal/db/activity/models.go @@ -29,6 +29,42 @@ type Activity struct { Error sql.NullString `json:"error"` } +type CryptoListing struct { + ID string `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt sql.NullTime `json:"deleted_at"` + ApiID string `json:"api_id"` + Name string `json:"name"` + Symbol string `json:"symbol"` + WebsiteSlug string `json:"website_slug"` +} + +type FearGreedIndex struct { + ID string `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt sql.NullTime `json:"deleted_at"` + Value int64 `json:"value"` + ValueClassification string `json:"value_classification"` + Timestamp time.Time `json:"timestamp"` + TimeUntilUpdate sql.NullString `json:"time_until_update"` +} + +type GlobalMarket struct { + ID string `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt sql.NullTime `json:"deleted_at"` + TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` + Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` + BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` + ActiveCurrencies sql.NullInt64 `json:"active_currencies"` + ActiveAssets sql.NullInt64 `json:"active_assets"` + ActiveMarkets sql.NullInt64 `json:"active_markets"` + LastUpdated time.Time `json:"last_updated"` +} + type Health struct { ID string `json:"id"` CreatedAt time.Time `json:"created_at"` diff --git a/internal/sink/activity/querier.go b/internal/db/activity/querier.go similarity index 54% rename from internal/sink/activity/querier.go rename to internal/db/activity/querier.go index 0943c26..b9b721e 100644 --- a/internal/sink/activity/querier.go +++ b/internal/db/activity/querier.go @@ -12,28 +12,50 @@ import ( type Querier interface { GetActivityByID(ctx context.Context, id string) (Activity, error) GetActivityByTxHash(ctx context.Context, txHash sql.NullString) (Activity, error) + GetCryptoListingByApiID(ctx context.Context, apiID string) (CryptoListing, error) + GetCryptoListingByID(ctx context.Context, id string) (CryptoListing, error) + GetCryptoListingBySymbol(ctx context.Context, symbol string) (CryptoListing, error) + GetCryptoListingByWebsiteSlug(ctx context.Context, websiteSlug string) (CryptoListing, error) + GetFearGreedIndexByID(ctx context.Context, id string) (FearGreedIndex, error) + GetGlobalMarketByID(ctx context.Context, id string) (GlobalMarket, error) GetHealthByEndpoint(ctx context.Context, endpointUrl string) (Health, error) GetHealthByID(ctx context.Context, id string) (Health, error) + GetLatestFearGreedIndex(ctx context.Context) (FearGreedIndex, error) + GetLatestGlobalMarket(ctx context.Context) (GlobalMarket, error) GetServiceByAddress(ctx context.Context, address string) (Service, error) GetServiceByChainAndAddress(ctx context.Context, arg GetServiceByChainAndAddressParams) (Service, error) GetServiceByID(ctx context.Context, id string) (Service, error) // ACTIVITY QUERIES InsertActivity(ctx context.Context, arg InsertActivityParams) (Activity, error) + // CRYPTO LISTINGS QUERIES (NEW) + InsertCryptoListing(ctx context.Context, arg InsertCryptoListingParams) (CryptoListing, error) + // FEAR AND GREED INDEX QUERIES (NEW) + InsertFearGreedIndex(ctx context.Context, arg InsertFearGreedIndexParams) (FearGreedIndex, error) + InsertGlobalMarket(ctx context.Context, arg InsertGlobalMarketParams) (GlobalMarket, error) // HEALTH QUERIES InsertHealth(ctx context.Context, arg InsertHealthParams) (Health, error) InsertService(ctx context.Context, arg InsertServiceParams) (Service, error) ListActivitiesByAccount(ctx context.Context, arg ListActivitiesByAccountParams) ([]Activity, error) ListActivitiesByStatus(ctx context.Context, arg ListActivitiesByStatusParams) ([]Activity, error) ListActivitiesByType(ctx context.Context, arg ListActivitiesByTypeParams) ([]Activity, error) + ListCryptoListings(ctx context.Context, arg ListCryptoListingsParams) ([]CryptoListing, error) + ListFearGreedIndexHistory(ctx context.Context, arg ListFearGreedIndexHistoryParams) ([]FearGreedIndex, error) + ListGlobalMarketHistory(ctx context.Context, arg ListGlobalMarketHistoryParams) ([]GlobalMarket, error) ListHealthByChain(ctx context.Context, arg ListHealthByChainParams) ([]Health, error) ListHealthByStatus(ctx context.Context, arg ListHealthByStatusParams) ([]Health, error) ListHealthChecksNeedingUpdate(ctx context.Context, limit int64) ([]Health, error) ListServicesByChain(ctx context.Context, arg ListServicesByChainParams) ([]Service, error) ListServicesByOwner(ctx context.Context, arg ListServicesByOwnerParams) ([]Service, error) SoftDeleteActivity(ctx context.Context, id string) error + SoftDeleteCryptoListing(ctx context.Context, id string) error + SoftDeleteFearGreedIndex(ctx context.Context, id string) error + SoftDeleteGlobalMarket(ctx context.Context, id string) error SoftDeleteHealth(ctx context.Context, id string) error SoftDeleteService(ctx context.Context, id string) error UpdateActivityStatus(ctx context.Context, arg UpdateActivityStatusParams) (Activity, error) + UpdateCryptoListing(ctx context.Context, arg UpdateCryptoListingParams) (CryptoListing, error) + UpdateFearGreedIndex(ctx context.Context, arg UpdateFearGreedIndexParams) (FearGreedIndex, error) + UpdateGlobalMarket(ctx context.Context, arg UpdateGlobalMarketParams) (GlobalMarket, error) UpdateHealthCheck(ctx context.Context, arg UpdateHealthCheckParams) (Health, error) UpdateService(ctx context.Context, arg UpdateServiceParams) (Service, error) } diff --git a/internal/sink/activity/query.sql b/internal/db/activity/query.sql similarity index 56% rename from internal/sink/activity/query.sql rename to internal/db/activity/query.sql index 7c69aea..cff4428 100644 --- a/internal/sink/activity/query.sql +++ b/internal/db/activity/query.sql @@ -189,3 +189,150 @@ RETURNING *; UPDATE health SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?; + + +-- name: GetGlobalMarketByID :one +SELECT * FROM global_market +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetLatestGlobalMarket :one +SELECT * FROM global_market +WHERE deleted_at IS NULL +ORDER BY last_updated DESC +LIMIT 1; + +-- name: ListGlobalMarketHistory :many +SELECT * FROM global_market +WHERE deleted_at IS NULL +ORDER BY last_updated DESC +LIMIT ? OFFSET ?; + +-- name: UpdateGlobalMarket :one +UPDATE global_market +SET + total_market_cap_usd = ?, + total_24h_volume_usd = ?, + bitcoin_percentage_of_market_cap = ?, + active_currencies = ?, + active_assets = ?, + active_markets = ?, + last_updated = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING *; + +-- name: SoftDeleteGlobalMarket :exec +UPDATE global_market +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ?; + +-- FEAR AND GREED INDEX QUERIES (NEW) +-- name: InsertFearGreedIndex :one +INSERT INTO fear_greed_index ( + value, + value_classification, + timestamp, + time_until_update +) VALUES (?, ?, ?, ?) +RETURNING *; + +-- name: GetFearGreedIndexByID :one +SELECT * FROM fear_greed_index +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetLatestFearGreedIndex :one +SELECT * FROM fear_greed_index +WHERE deleted_at IS NULL +ORDER BY timestamp DESC +LIMIT 1; + +-- name: ListFearGreedIndexHistory :many +SELECT * FROM fear_greed_index +WHERE deleted_at IS NULL +ORDER BY timestamp DESC +LIMIT ? OFFSET ?; + +-- name: UpdateFearGreedIndex :one +UPDATE fear_greed_index +SET + value = ?, + value_classification = ?, + timestamp = ?, + time_until_update = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING *; + +-- name: InsertGlobalMarket :one +INSERT INTO global_market ( + total_market_cap_usd, + total_24h_volume_usd, + bitcoin_percentage_of_market_cap, + active_currencies, + active_assets, + active_markets, + last_updated +) VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING *; + + +-- name: SoftDeleteFearGreedIndex :exec +UPDATE fear_greed_index +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ?; + +-- CRYPTO LISTINGS QUERIES (NEW) +-- name: InsertCryptoListing :one +INSERT INTO crypto_listings ( + api_id, + name, + symbol, + website_slug +) VALUES (?, ?, ?, ?) +RETURNING *; + +-- name: GetCryptoListingByID :one +SELECT * FROM crypto_listings +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetCryptoListingByApiID :one +SELECT * FROM crypto_listings +WHERE api_id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetCryptoListingBySymbol :one +SELECT * FROM crypto_listings +WHERE symbol = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetCryptoListingByWebsiteSlug :one +SELECT * FROM crypto_listings +WHERE website_slug = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: ListCryptoListings :many +SELECT * FROM crypto_listings +WHERE deleted_at IS NULL +ORDER BY name ASC +LIMIT ? OFFSET ?; + +-- name: UpdateCryptoListing :one +UPDATE crypto_listings +SET + name = ?, + symbol = ?, + website_slug = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING *; + +-- name: SoftDeleteCryptoListing :exec +UPDATE crypto_listings +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ?; diff --git a/internal/sink/activity/query.sql.go b/internal/db/activity/query.sql.go similarity index 60% rename from internal/sink/activity/query.sql.go rename to internal/db/activity/query.sql.go index 275270d..749ea91 100644 --- a/internal/sink/activity/query.sql.go +++ b/internal/db/activity/query.sql.go @@ -73,6 +73,141 @@ func (q *Queries) GetActivityByTxHash(ctx context.Context, txHash sql.NullString return i, err } +const getCryptoListingByApiID = `-- name: GetCryptoListingByApiID :one +SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings +WHERE api_id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetCryptoListingByApiID(ctx context.Context, apiID string) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, getCryptoListingByApiID, apiID) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const getCryptoListingByID = `-- name: GetCryptoListingByID :one +SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetCryptoListingByID(ctx context.Context, id string) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, getCryptoListingByID, id) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const getCryptoListingBySymbol = `-- name: GetCryptoListingBySymbol :one +SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings +WHERE symbol = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetCryptoListingBySymbol(ctx context.Context, symbol string) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, getCryptoListingBySymbol, symbol) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const getCryptoListingByWebsiteSlug = `-- name: GetCryptoListingByWebsiteSlug :one +SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings +WHERE website_slug = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetCryptoListingByWebsiteSlug(ctx context.Context, websiteSlug string) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, getCryptoListingByWebsiteSlug, websiteSlug) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const getFearGreedIndexByID = `-- name: GetFearGreedIndexByID :one +SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetFearGreedIndexByID(ctx context.Context, id string) (FearGreedIndex, error) { + row := q.db.QueryRowContext(ctx, getFearGreedIndexByID, id) + var i FearGreedIndex + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Value, + &i.ValueClassification, + &i.Timestamp, + &i.TimeUntilUpdate, + ) + return i, err +} + +const getGlobalMarketByID = `-- name: GetGlobalMarketByID :one +SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetGlobalMarketByID(ctx context.Context, id string) (GlobalMarket, error) { + row := q.db.QueryRowContext(ctx, getGlobalMarketByID, id) + var i GlobalMarket + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TotalMarketCapUsd, + &i.Total24hVolumeUsd, + &i.BitcoinPercentageOfMarketCap, + &i.ActiveCurrencies, + &i.ActiveAssets, + &i.ActiveMarkets, + &i.LastUpdated, + ) + return i, err +} + const getHealthByEndpoint = `-- name: GetHealthByEndpoint :one SELECT id, created_at, updated_at, deleted_at, endpoint_url, endpoint_type, chain_id, status, response_time_ms, last_checked, next_check, failure_count, success_count, response_data, error_message FROM health WHERE endpoint_url = ? AND deleted_at IS NULL @@ -132,6 +267,55 @@ func (q *Queries) GetHealthByID(ctx context.Context, id string) (Health, error) return i, err } +const getLatestFearGreedIndex = `-- name: GetLatestFearGreedIndex :one +SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index +WHERE deleted_at IS NULL +ORDER BY timestamp DESC +LIMIT 1 +` + +func (q *Queries) GetLatestFearGreedIndex(ctx context.Context) (FearGreedIndex, error) { + row := q.db.QueryRowContext(ctx, getLatestFearGreedIndex) + var i FearGreedIndex + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Value, + &i.ValueClassification, + &i.Timestamp, + &i.TimeUntilUpdate, + ) + return i, err +} + +const getLatestGlobalMarket = `-- name: GetLatestGlobalMarket :one +SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market +WHERE deleted_at IS NULL +ORDER BY last_updated DESC +LIMIT 1 +` + +func (q *Queries) GetLatestGlobalMarket(ctx context.Context) (GlobalMarket, error) { + row := q.db.QueryRowContext(ctx, getLatestGlobalMarket) + var i GlobalMarket + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TotalMarketCapUsd, + &i.Total24hVolumeUsd, + &i.BitcoinPercentageOfMarketCap, + &i.ActiveCurrencies, + &i.ActiveAssets, + &i.ActiveMarkets, + &i.LastUpdated, + ) + return i, err +} + const getServiceByAddress = `-- name: GetServiceByAddress :one SELECT id, created_at, updated_at, deleted_at, name, description, chain_id, address, owner_address, metadata, status, block_height FROM services WHERE address = ? AND deleted_at IS NULL @@ -290,6 +474,134 @@ func (q *Queries) InsertActivity(ctx context.Context, arg InsertActivityParams) return i, err } +const insertCryptoListing = `-- name: InsertCryptoListing :one +INSERT INTO crypto_listings ( + api_id, + name, + symbol, + website_slug +) VALUES (?, ?, ?, ?) +RETURNING id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug +` + +type InsertCryptoListingParams struct { + ApiID string `json:"api_id"` + Name string `json:"name"` + Symbol string `json:"symbol"` + WebsiteSlug string `json:"website_slug"` +} + +// CRYPTO LISTINGS QUERIES (NEW) +func (q *Queries) InsertCryptoListing(ctx context.Context, arg InsertCryptoListingParams) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, insertCryptoListing, + arg.ApiID, + arg.Name, + arg.Symbol, + arg.WebsiteSlug, + ) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const insertFearGreedIndex = `-- name: InsertFearGreedIndex :one +INSERT INTO fear_greed_index ( + value, + value_classification, + timestamp, + time_until_update +) VALUES (?, ?, ?, ?) +RETURNING id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update +` + +type InsertFearGreedIndexParams struct { + Value int64 `json:"value"` + ValueClassification string `json:"value_classification"` + Timestamp time.Time `json:"timestamp"` + TimeUntilUpdate sql.NullString `json:"time_until_update"` +} + +// FEAR AND GREED INDEX QUERIES (NEW) +func (q *Queries) InsertFearGreedIndex(ctx context.Context, arg InsertFearGreedIndexParams) (FearGreedIndex, error) { + row := q.db.QueryRowContext(ctx, insertFearGreedIndex, + arg.Value, + arg.ValueClassification, + arg.Timestamp, + arg.TimeUntilUpdate, + ) + var i FearGreedIndex + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Value, + &i.ValueClassification, + &i.Timestamp, + &i.TimeUntilUpdate, + ) + return i, err +} + +const insertGlobalMarket = `-- name: InsertGlobalMarket :one +INSERT INTO global_market ( + total_market_cap_usd, + total_24h_volume_usd, + bitcoin_percentage_of_market_cap, + active_currencies, + active_assets, + active_markets, + last_updated +) VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated +` + +type InsertGlobalMarketParams struct { + TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` + Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` + BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` + ActiveCurrencies sql.NullInt64 `json:"active_currencies"` + ActiveAssets sql.NullInt64 `json:"active_assets"` + ActiveMarkets sql.NullInt64 `json:"active_markets"` + LastUpdated time.Time `json:"last_updated"` +} + +func (q *Queries) InsertGlobalMarket(ctx context.Context, arg InsertGlobalMarketParams) (GlobalMarket, error) { + row := q.db.QueryRowContext(ctx, insertGlobalMarket, + arg.TotalMarketCapUsd, + arg.Total24hVolumeUsd, + arg.BitcoinPercentageOfMarketCap, + arg.ActiveCurrencies, + arg.ActiveAssets, + arg.ActiveMarkets, + arg.LastUpdated, + ) + var i GlobalMarket + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TotalMarketCapUsd, + &i.Total24hVolumeUsd, + &i.BitcoinPercentageOfMarketCap, + &i.ActiveCurrencies, + &i.ActiveAssets, + &i.ActiveMarkets, + &i.LastUpdated, + ) + return i, err +} + const insertHealth = `-- name: InsertHealth :one INSERT INTO health ( endpoint_url, @@ -573,6 +885,141 @@ func (q *Queries) ListActivitiesByType(ctx context.Context, arg ListActivitiesBy return items, nil } +const listCryptoListings = `-- name: ListCryptoListings :many +SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings +WHERE deleted_at IS NULL +ORDER BY name ASC +LIMIT ? OFFSET ? +` + +type ListCryptoListingsParams struct { + Limit int64 `json:"limit"` + Offset int64 `json:"offset"` +} + +func (q *Queries) ListCryptoListings(ctx context.Context, arg ListCryptoListingsParams) ([]CryptoListing, error) { + rows, err := q.db.QueryContext(ctx, listCryptoListings, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CryptoListing + for rows.Next() { + var i CryptoListing + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listFearGreedIndexHistory = `-- name: ListFearGreedIndexHistory :many +SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index +WHERE deleted_at IS NULL +ORDER BY timestamp DESC +LIMIT ? OFFSET ? +` + +type ListFearGreedIndexHistoryParams struct { + Limit int64 `json:"limit"` + Offset int64 `json:"offset"` +} + +func (q *Queries) ListFearGreedIndexHistory(ctx context.Context, arg ListFearGreedIndexHistoryParams) ([]FearGreedIndex, error) { + rows, err := q.db.QueryContext(ctx, listFearGreedIndexHistory, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FearGreedIndex + for rows.Next() { + var i FearGreedIndex + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Value, + &i.ValueClassification, + &i.Timestamp, + &i.TimeUntilUpdate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listGlobalMarketHistory = `-- name: ListGlobalMarketHistory :many +SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market +WHERE deleted_at IS NULL +ORDER BY last_updated DESC +LIMIT ? OFFSET ? +` + +type ListGlobalMarketHistoryParams struct { + Limit int64 `json:"limit"` + Offset int64 `json:"offset"` +} + +func (q *Queries) ListGlobalMarketHistory(ctx context.Context, arg ListGlobalMarketHistoryParams) ([]GlobalMarket, error) { + rows, err := q.db.QueryContext(ctx, listGlobalMarketHistory, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GlobalMarket + for rows.Next() { + var i GlobalMarket + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TotalMarketCapUsd, + &i.Total24hVolumeUsd, + &i.BitcoinPercentageOfMarketCap, + &i.ActiveCurrencies, + &i.ActiveAssets, + &i.ActiveMarkets, + &i.LastUpdated, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listHealthByChain = `-- name: ListHealthByChain :many SELECT id, created_at, updated_at, deleted_at, endpoint_url, endpoint_type, chain_id, status, response_time_ms, last_checked, next_check, failure_count, success_count, response_data, error_message FROM health WHERE chain_id = ? AND deleted_at IS NULL @@ -832,6 +1279,39 @@ func (q *Queries) SoftDeleteActivity(ctx context.Context, id string) error { return err } +const softDeleteCryptoListing = `-- name: SoftDeleteCryptoListing :exec +UPDATE crypto_listings +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ? +` + +func (q *Queries) SoftDeleteCryptoListing(ctx context.Context, id string) error { + _, err := q.db.ExecContext(ctx, softDeleteCryptoListing, id) + return err +} + +const softDeleteFearGreedIndex = `-- name: SoftDeleteFearGreedIndex :exec +UPDATE fear_greed_index +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ? +` + +func (q *Queries) SoftDeleteFearGreedIndex(ctx context.Context, id string) error { + _, err := q.db.ExecContext(ctx, softDeleteFearGreedIndex, id) + return err +} + +const softDeleteGlobalMarket = `-- name: SoftDeleteGlobalMarket :exec +UPDATE global_market +SET deleted_at = CURRENT_TIMESTAMP +WHERE id = ? +` + +func (q *Queries) SoftDeleteGlobalMarket(ctx context.Context, id string) error { + _, err := q.db.ExecContext(ctx, softDeleteGlobalMarket, id) + return err +} + const softDeleteHealth = `-- name: SoftDeleteHealth :exec UPDATE health SET deleted_at = CURRENT_TIMESTAMP @@ -912,6 +1392,144 @@ func (q *Queries) UpdateActivityStatus(ctx context.Context, arg UpdateActivitySt return i, err } +const updateCryptoListing = `-- name: UpdateCryptoListing :one +UPDATE crypto_listings +SET + name = ?, + symbol = ?, + website_slug = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug +` + +type UpdateCryptoListingParams struct { + Name string `json:"name"` + Symbol string `json:"symbol"` + WebsiteSlug string `json:"website_slug"` + ID string `json:"id"` +} + +func (q *Queries) UpdateCryptoListing(ctx context.Context, arg UpdateCryptoListingParams) (CryptoListing, error) { + row := q.db.QueryRowContext(ctx, updateCryptoListing, + arg.Name, + arg.Symbol, + arg.WebsiteSlug, + arg.ID, + ) + var i CryptoListing + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.ApiID, + &i.Name, + &i.Symbol, + &i.WebsiteSlug, + ) + return i, err +} + +const updateFearGreedIndex = `-- name: UpdateFearGreedIndex :one +UPDATE fear_greed_index +SET + value = ?, + value_classification = ?, + timestamp = ?, + time_until_update = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update +` + +type UpdateFearGreedIndexParams struct { + Value int64 `json:"value"` + ValueClassification string `json:"value_classification"` + Timestamp time.Time `json:"timestamp"` + TimeUntilUpdate sql.NullString `json:"time_until_update"` + ID string `json:"id"` +} + +func (q *Queries) UpdateFearGreedIndex(ctx context.Context, arg UpdateFearGreedIndexParams) (FearGreedIndex, error) { + row := q.db.QueryRowContext(ctx, updateFearGreedIndex, + arg.Value, + arg.ValueClassification, + arg.Timestamp, + arg.TimeUntilUpdate, + arg.ID, + ) + var i FearGreedIndex + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Value, + &i.ValueClassification, + &i.Timestamp, + &i.TimeUntilUpdate, + ) + return i, err +} + +const updateGlobalMarket = `-- name: UpdateGlobalMarket :one +UPDATE global_market +SET + total_market_cap_usd = ?, + total_24h_volume_usd = ?, + bitcoin_percentage_of_market_cap = ?, + active_currencies = ?, + active_assets = ?, + active_markets = ?, + last_updated = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +AND deleted_at IS NULL +RETURNING id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated +` + +type UpdateGlobalMarketParams struct { + TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` + Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` + BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` + ActiveCurrencies sql.NullInt64 `json:"active_currencies"` + ActiveAssets sql.NullInt64 `json:"active_assets"` + ActiveMarkets sql.NullInt64 `json:"active_markets"` + LastUpdated time.Time `json:"last_updated"` + ID string `json:"id"` +} + +func (q *Queries) UpdateGlobalMarket(ctx context.Context, arg UpdateGlobalMarketParams) (GlobalMarket, error) { + row := q.db.QueryRowContext(ctx, updateGlobalMarket, + arg.TotalMarketCapUsd, + arg.Total24hVolumeUsd, + arg.BitcoinPercentageOfMarketCap, + arg.ActiveCurrencies, + arg.ActiveAssets, + arg.ActiveMarkets, + arg.LastUpdated, + arg.ID, + ) + var i GlobalMarket + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TotalMarketCapUsd, + &i.Total24hVolumeUsd, + &i.BitcoinPercentageOfMarketCap, + &i.ActiveCurrencies, + &i.ActiveAssets, + &i.ActiveMarkets, + &i.LastUpdated, + ) + return i, err +} + const updateHealthCheck = `-- name: UpdateHealthCheck :one UPDATE health SET diff --git a/internal/sink/activity/schema.sql b/internal/db/activity/schema.sql similarity index 61% rename from internal/sink/activity/schema.sql rename to internal/db/activity/schema.sql index e280e7e..1c64724 100644 --- a/internal/sink/activity/schema.sql +++ b/internal/db/activity/schema.sql @@ -59,6 +59,47 @@ CREATE TABLE health ( FOREIGN KEY (chain_id) REFERENCES assets(chain_id) ); +-- Global market data from Alternative.me API +CREATE TABLE global_market ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + total_market_cap_usd REAL, + total_24h_volume_usd REAL, + bitcoin_percentage_of_market_cap REAL, + active_currencies INTEGER, + active_assets INTEGER, + active_markets INTEGER, + last_updated TIMESTAMP NOT NULL +); + +-- Fear and Greed Index data from Alternative.me +CREATE TABLE fear_greed_index ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + value INTEGER NOT NULL, + value_classification TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL, + time_until_update TEXT +); + +-- Listings data from Alternative.me API +CREATE TABLE crypto_listings ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + api_id TEXT NOT NULL, + name TEXT NOT NULL, + symbol TEXT NOT NULL, + website_slug TEXT NOT NULL, + UNIQUE(api_id) +); + + CREATE INDEX idx_services_name ON services(name); CREATE INDEX idx_services_chain_id ON services(chain_id); CREATE INDEX idx_services_address ON services(address); @@ -82,3 +123,14 @@ CREATE INDEX idx_health_last_checked ON health(last_checked); CREATE INDEX idx_health_next_check ON health(next_check); CREATE INDEX idx_health_deleted_at ON health(deleted_at); +CREATE INDEX idx_global_market_last_updated ON global_market(last_updated); +CREATE INDEX idx_global_market_deleted_at ON global_market(deleted_at); + +CREATE INDEX idx_fear_greed_index_timestamp ON fear_greed_index(timestamp); +CREATE INDEX idx_fear_greed_index_value ON fear_greed_index(value); +CREATE INDEX idx_fear_greed_index_deleted_at ON fear_greed_index(deleted_at); + +CREATE INDEX idx_crypto_listings_api_id ON crypto_listings(api_id); +CREATE INDEX idx_crypto_listings_symbol ON crypto_listings(symbol); +CREATE INDEX idx_crypto_listings_website_slug ON crypto_listings(website_slug); +CREATE INDEX idx_crypto_listings_deleted_at ON crypto_listings(deleted_at); diff --git a/internal/sink/network/db.go b/internal/db/network/db.go similarity index 100% rename from internal/sink/network/db.go rename to internal/db/network/db.go diff --git a/internal/sink/network/models.go b/internal/db/network/models.go similarity index 72% rename from internal/sink/network/models.go rename to internal/db/network/models.go index 29423ec..23bcd7b 100644 --- a/internal/sink/network/models.go +++ b/internal/db/network/models.go @@ -62,42 +62,6 @@ type Blockchain struct { Forum sql.NullString `json:"forum"` } -type CryptoListing struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt sql.NullTime `json:"deleted_at"` - ApiID string `json:"api_id"` - Name string `json:"name"` - Symbol string `json:"symbol"` - WebsiteSlug string `json:"website_slug"` -} - -type FearGreedIndex struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt sql.NullTime `json:"deleted_at"` - Value int64 `json:"value"` - ValueClassification string `json:"value_classification"` - Timestamp time.Time `json:"timestamp"` - TimeUntilUpdate sql.NullString `json:"time_until_update"` -} - -type GlobalMarket struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt sql.NullTime `json:"deleted_at"` - TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` - Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` - BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` - ActiveCurrencies sql.NullInt64 `json:"active_currencies"` - ActiveAssets sql.NullInt64 `json:"active_assets"` - ActiveMarkets sql.NullInt64 `json:"active_markets"` - LastUpdated time.Time `json:"last_updated"` -} - type Price struct { ID string `json:"id"` CreatedAt time.Time `json:"created_at"` diff --git a/internal/sink/network/querier.go b/internal/db/network/querier.go similarity index 69% rename from internal/sink/network/querier.go rename to internal/db/network/querier.go index 69ac647..53cd420 100644 --- a/internal/sink/network/querier.go +++ b/internal/db/network/querier.go @@ -22,14 +22,6 @@ type Querier interface { GetBlockchainEndpoints(ctx context.Context, id string) (GetBlockchainEndpointsRow, error) GetBlockchainExplorer(ctx context.Context, id string) (GetBlockchainExplorerRow, error) GetBlockchainWithAssetInfo(ctx context.Context, id string) (GetBlockchainWithAssetInfoRow, error) - GetCryptoListingByApiID(ctx context.Context, apiID string) (CryptoListing, error) - GetCryptoListingByID(ctx context.Context, id string) (CryptoListing, error) - GetCryptoListingBySymbol(ctx context.Context, symbol string) (CryptoListing, error) - GetCryptoListingByWebsiteSlug(ctx context.Context, websiteSlug string) (CryptoListing, error) - GetFearGreedIndexByID(ctx context.Context, id string) (FearGreedIndex, error) - GetGlobalMarketByID(ctx context.Context, id string) (GlobalMarket, error) - GetLatestFearGreedIndex(ctx context.Context) (FearGreedIndex, error) - GetLatestGlobalMarket(ctx context.Context) (GlobalMarket, error) GetPriceByAssetID(ctx context.Context, assetID string) (Price, error) GetPriceByID(ctx context.Context, id string) (Price, error) GetPriceConversionByCurrency(ctx context.Context, arg GetPriceConversionByCurrencyParams) (PriceConversion, error) @@ -39,12 +31,6 @@ type Querier interface { InsertAsset(ctx context.Context, arg InsertAssetParams) (Asset, error) // BLOCKCHAIN QUERIES InsertBlockchain(ctx context.Context, arg InsertBlockchainParams) (Blockchain, error) - // CRYPTO LISTINGS QUERIES (NEW) - InsertCryptoListing(ctx context.Context, arg InsertCryptoListingParams) (CryptoListing, error) - // FEAR AND GREED INDEX QUERIES (NEW) - InsertFearGreedIndex(ctx context.Context, arg InsertFearGreedIndexParams) (FearGreedIndex, error) - // GLOBAL MARKET QUERIES (NEW) - InsertGlobalMarket(ctx context.Context, arg InsertGlobalMarketParams) (GlobalMarket, error) // PRICE QUERIES (UPDATED) InsertPrice(ctx context.Context, arg InsertPriceParams) (Price, error) // PRICE CONVERSION QUERIES (NEW) @@ -58,16 +44,10 @@ type Querier interface { ListBlockchainsWithExtensionSupport(ctx context.Context) ([]Blockchain, error) ListBlockchainsWithMobileSupport(ctx context.Context) ([]Blockchain, error) ListBlockchainsWithStaking(ctx context.Context) ([]Blockchain, error) - ListCryptoListings(ctx context.Context, arg ListCryptoListingsParams) ([]CryptoListing, error) - ListFearGreedIndexHistory(ctx context.Context, arg ListFearGreedIndexHistoryParams) ([]FearGreedIndex, error) - ListGlobalMarketHistory(ctx context.Context, arg ListGlobalMarketHistoryParams) ([]GlobalMarket, error) ListPriceHistoryByAssetID(ctx context.Context, arg ListPriceHistoryByAssetIDParams) ([]Price, error) SearchBlockchains(ctx context.Context, arg SearchBlockchainsParams) ([]Blockchain, error) SoftDeleteAsset(ctx context.Context, id string) error SoftDeleteBlockchain(ctx context.Context, id string) error - SoftDeleteCryptoListing(ctx context.Context, id string) error - SoftDeleteFearGreedIndex(ctx context.Context, id string) error - SoftDeleteGlobalMarket(ctx context.Context, id string) error SoftDeletePriceConversion(ctx context.Context, id string) error UpdateAsset(ctx context.Context, arg UpdateAssetParams) (Asset, error) UpdateBlockchain(ctx context.Context, arg UpdateBlockchainParams) (Blockchain, error) @@ -77,9 +57,6 @@ type Querier interface { UpdateBlockchainFeeInfo(ctx context.Context, arg UpdateBlockchainFeeInfoParams) (Blockchain, error) UpdateBlockchainImages(ctx context.Context, arg UpdateBlockchainImagesParams) (Blockchain, error) UpdateBlockchainSocialLinks(ctx context.Context, arg UpdateBlockchainSocialLinksParams) (Blockchain, error) - UpdateCryptoListing(ctx context.Context, arg UpdateCryptoListingParams) (CryptoListing, error) - UpdateFearGreedIndex(ctx context.Context, arg UpdateFearGreedIndexParams) (FearGreedIndex, error) - UpdateGlobalMarket(ctx context.Context, arg UpdateGlobalMarketParams) (GlobalMarket, error) UpdatePrice(ctx context.Context, arg UpdatePriceParams) (Price, error) UpdatePriceConversion(ctx context.Context, arg UpdatePriceConversionParams) (PriceConversion, error) } diff --git a/internal/sink/network/query.sql b/internal/db/network/query.sql similarity index 77% rename from internal/sink/network/query.sql rename to internal/db/network/query.sql index dfdf621..b228615 100644 --- a/internal/sink/network/query.sql +++ b/internal/db/network/query.sql @@ -190,152 +190,6 @@ UPDATE price_conversions SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?; --- GLOBAL MARKET QUERIES (NEW) --- name: InsertGlobalMarket :one -INSERT INTO global_market ( - total_market_cap_usd, - total_24h_volume_usd, - bitcoin_percentage_of_market_cap, - active_currencies, - active_assets, - active_markets, - last_updated -) VALUES (?, ?, ?, ?, ?, ?, ?) -RETURNING *; - --- name: GetGlobalMarketByID :one -SELECT * FROM global_market -WHERE id = ? AND deleted_at IS NULL -LIMIT 1; - --- name: GetLatestGlobalMarket :one -SELECT * FROM global_market -WHERE deleted_at IS NULL -ORDER BY last_updated DESC -LIMIT 1; - --- name: ListGlobalMarketHistory :many -SELECT * FROM global_market -WHERE deleted_at IS NULL -ORDER BY last_updated DESC -LIMIT ? OFFSET ?; - --- name: UpdateGlobalMarket :one -UPDATE global_market -SET - total_market_cap_usd = ?, - total_24h_volume_usd = ?, - bitcoin_percentage_of_market_cap = ?, - active_currencies = ?, - active_assets = ?, - active_markets = ?, - last_updated = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING *; - --- name: SoftDeleteGlobalMarket :exec -UPDATE global_market -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ?; - --- FEAR AND GREED INDEX QUERIES (NEW) --- name: InsertFearGreedIndex :one -INSERT INTO fear_greed_index ( - value, - value_classification, - timestamp, - time_until_update -) VALUES (?, ?, ?, ?) -RETURNING *; - --- name: GetFearGreedIndexByID :one -SELECT * FROM fear_greed_index -WHERE id = ? AND deleted_at IS NULL -LIMIT 1; - --- name: GetLatestFearGreedIndex :one -SELECT * FROM fear_greed_index -WHERE deleted_at IS NULL -ORDER BY timestamp DESC -LIMIT 1; - --- name: ListFearGreedIndexHistory :many -SELECT * FROM fear_greed_index -WHERE deleted_at IS NULL -ORDER BY timestamp DESC -LIMIT ? OFFSET ?; - --- name: UpdateFearGreedIndex :one -UPDATE fear_greed_index -SET - value = ?, - value_classification = ?, - timestamp = ?, - time_until_update = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING *; - --- name: SoftDeleteFearGreedIndex :exec -UPDATE fear_greed_index -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ?; - --- CRYPTO LISTINGS QUERIES (NEW) --- name: InsertCryptoListing :one -INSERT INTO crypto_listings ( - api_id, - name, - symbol, - website_slug -) VALUES (?, ?, ?, ?) -RETURNING *; - --- name: GetCryptoListingByID :one -SELECT * FROM crypto_listings -WHERE id = ? AND deleted_at IS NULL -LIMIT 1; - --- name: GetCryptoListingByApiID :one -SELECT * FROM crypto_listings -WHERE api_id = ? AND deleted_at IS NULL -LIMIT 1; - --- name: GetCryptoListingBySymbol :one -SELECT * FROM crypto_listings -WHERE symbol = ? AND deleted_at IS NULL -LIMIT 1; - --- name: GetCryptoListingByWebsiteSlug :one -SELECT * FROM crypto_listings -WHERE website_slug = ? AND deleted_at IS NULL -LIMIT 1; - --- name: ListCryptoListings :many -SELECT * FROM crypto_listings -WHERE deleted_at IS NULL -ORDER BY name ASC -LIMIT ? OFFSET ?; - --- name: UpdateCryptoListing :one -UPDATE crypto_listings -SET - name = ?, - symbol = ?, - website_slug = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING *; - --- name: SoftDeleteCryptoListing :exec -UPDATE crypto_listings -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ?; - -- BLOCKCHAIN QUERIES -- name: InsertBlockchain :one INSERT INTO blockchains ( diff --git a/internal/sink/network/query.sql.go b/internal/db/network/query.sql.go similarity index 82% rename from internal/sink/network/query.sql.go rename to internal/db/network/query.sql.go index 9e93c8a..936da57 100644 --- a/internal/sink/network/query.sql.go +++ b/internal/db/network/query.sql.go @@ -541,190 +541,6 @@ func (q *Queries) GetBlockchainWithAssetInfo(ctx context.Context, id string) (Ge return i, err } -const getCryptoListingByApiID = `-- name: GetCryptoListingByApiID :one -SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings -WHERE api_id = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetCryptoListingByApiID(ctx context.Context, apiID string) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, getCryptoListingByApiID, apiID) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const getCryptoListingByID = `-- name: GetCryptoListingByID :one -SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings -WHERE id = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetCryptoListingByID(ctx context.Context, id string) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, getCryptoListingByID, id) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const getCryptoListingBySymbol = `-- name: GetCryptoListingBySymbol :one -SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings -WHERE symbol = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetCryptoListingBySymbol(ctx context.Context, symbol string) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, getCryptoListingBySymbol, symbol) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const getCryptoListingByWebsiteSlug = `-- name: GetCryptoListingByWebsiteSlug :one -SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings -WHERE website_slug = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetCryptoListingByWebsiteSlug(ctx context.Context, websiteSlug string) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, getCryptoListingByWebsiteSlug, websiteSlug) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const getFearGreedIndexByID = `-- name: GetFearGreedIndexByID :one -SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index -WHERE id = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetFearGreedIndexByID(ctx context.Context, id string) (FearGreedIndex, error) { - row := q.db.QueryRowContext(ctx, getFearGreedIndexByID, id) - var i FearGreedIndex - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.Value, - &i.ValueClassification, - &i.Timestamp, - &i.TimeUntilUpdate, - ) - return i, err -} - -const getGlobalMarketByID = `-- name: GetGlobalMarketByID :one -SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market -WHERE id = ? AND deleted_at IS NULL -LIMIT 1 -` - -func (q *Queries) GetGlobalMarketByID(ctx context.Context, id string) (GlobalMarket, error) { - row := q.db.QueryRowContext(ctx, getGlobalMarketByID, id) - var i GlobalMarket - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.TotalMarketCapUsd, - &i.Total24hVolumeUsd, - &i.BitcoinPercentageOfMarketCap, - &i.ActiveCurrencies, - &i.ActiveAssets, - &i.ActiveMarkets, - &i.LastUpdated, - ) - return i, err -} - -const getLatestFearGreedIndex = `-- name: GetLatestFearGreedIndex :one -SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index -WHERE deleted_at IS NULL -ORDER BY timestamp DESC -LIMIT 1 -` - -func (q *Queries) GetLatestFearGreedIndex(ctx context.Context) (FearGreedIndex, error) { - row := q.db.QueryRowContext(ctx, getLatestFearGreedIndex) - var i FearGreedIndex - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.Value, - &i.ValueClassification, - &i.Timestamp, - &i.TimeUntilUpdate, - ) - return i, err -} - -const getLatestGlobalMarket = `-- name: GetLatestGlobalMarket :one -SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market -WHERE deleted_at IS NULL -ORDER BY last_updated DESC -LIMIT 1 -` - -func (q *Queries) GetLatestGlobalMarket(ctx context.Context) (GlobalMarket, error) { - row := q.db.QueryRowContext(ctx, getLatestGlobalMarket) - var i GlobalMarket - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.TotalMarketCapUsd, - &i.Total24hVolumeUsd, - &i.BitcoinPercentageOfMarketCap, - &i.ActiveCurrencies, - &i.ActiveAssets, - &i.ActiveMarkets, - &i.LastUpdated, - ) - return i, err -} - const getPriceByAssetID = `-- name: GetPriceByAssetID :one SELECT id, created_at, updated_at, deleted_at, asset_id, price_usd, price_btc, volume_24h_usd, market_cap_usd, available_supply, total_supply, max_supply, percent_change_1h, percent_change_24h, percent_change_7d, rank, last_updated FROM prices WHERE asset_id = ? AND deleted_at IS NULL @@ -1088,135 +904,6 @@ func (q *Queries) InsertBlockchain(ctx context.Context, arg InsertBlockchainPara return i, err } -const insertCryptoListing = `-- name: InsertCryptoListing :one -INSERT INTO crypto_listings ( - api_id, - name, - symbol, - website_slug -) VALUES (?, ?, ?, ?) -RETURNING id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug -` - -type InsertCryptoListingParams struct { - ApiID string `json:"api_id"` - Name string `json:"name"` - Symbol string `json:"symbol"` - WebsiteSlug string `json:"website_slug"` -} - -// CRYPTO LISTINGS QUERIES (NEW) -func (q *Queries) InsertCryptoListing(ctx context.Context, arg InsertCryptoListingParams) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, insertCryptoListing, - arg.ApiID, - arg.Name, - arg.Symbol, - arg.WebsiteSlug, - ) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const insertFearGreedIndex = `-- name: InsertFearGreedIndex :one -INSERT INTO fear_greed_index ( - value, - value_classification, - timestamp, - time_until_update -) VALUES (?, ?, ?, ?) -RETURNING id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update -` - -type InsertFearGreedIndexParams struct { - Value int64 `json:"value"` - ValueClassification string `json:"value_classification"` - Timestamp time.Time `json:"timestamp"` - TimeUntilUpdate sql.NullString `json:"time_until_update"` -} - -// FEAR AND GREED INDEX QUERIES (NEW) -func (q *Queries) InsertFearGreedIndex(ctx context.Context, arg InsertFearGreedIndexParams) (FearGreedIndex, error) { - row := q.db.QueryRowContext(ctx, insertFearGreedIndex, - arg.Value, - arg.ValueClassification, - arg.Timestamp, - arg.TimeUntilUpdate, - ) - var i FearGreedIndex - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.Value, - &i.ValueClassification, - &i.Timestamp, - &i.TimeUntilUpdate, - ) - return i, err -} - -const insertGlobalMarket = `-- name: InsertGlobalMarket :one -INSERT INTO global_market ( - total_market_cap_usd, - total_24h_volume_usd, - bitcoin_percentage_of_market_cap, - active_currencies, - active_assets, - active_markets, - last_updated -) VALUES (?, ?, ?, ?, ?, ?, ?) -RETURNING id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated -` - -type InsertGlobalMarketParams struct { - TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` - Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` - BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` - ActiveCurrencies sql.NullInt64 `json:"active_currencies"` - ActiveAssets sql.NullInt64 `json:"active_assets"` - ActiveMarkets sql.NullInt64 `json:"active_markets"` - LastUpdated time.Time `json:"last_updated"` -} - -// GLOBAL MARKET QUERIES (NEW) -func (q *Queries) InsertGlobalMarket(ctx context.Context, arg InsertGlobalMarketParams) (GlobalMarket, error) { - row := q.db.QueryRowContext(ctx, insertGlobalMarket, - arg.TotalMarketCapUsd, - arg.Total24hVolumeUsd, - arg.BitcoinPercentageOfMarketCap, - arg.ActiveCurrencies, - arg.ActiveAssets, - arg.ActiveMarkets, - arg.LastUpdated, - ) - var i GlobalMarket - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.TotalMarketCapUsd, - &i.Total24hVolumeUsd, - &i.BitcoinPercentageOfMarketCap, - &i.ActiveCurrencies, - &i.ActiveAssets, - &i.ActiveMarkets, - &i.LastUpdated, - ) - return i, err -} - const insertPrice = `-- name: InsertPrice :one INSERT INTO prices ( asset_id, @@ -2010,141 +1697,6 @@ func (q *Queries) ListBlockchainsWithStaking(ctx context.Context) ([]Blockchain, return items, nil } -const listCryptoListings = `-- name: ListCryptoListings :many -SELECT id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug FROM crypto_listings -WHERE deleted_at IS NULL -ORDER BY name ASC -LIMIT ? OFFSET ? -` - -type ListCryptoListingsParams struct { - Limit int64 `json:"limit"` - Offset int64 `json:"offset"` -} - -func (q *Queries) ListCryptoListings(ctx context.Context, arg ListCryptoListingsParams) ([]CryptoListing, error) { - rows, err := q.db.QueryContext(ctx, listCryptoListings, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CryptoListing - for rows.Next() { - var i CryptoListing - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listFearGreedIndexHistory = `-- name: ListFearGreedIndexHistory :many -SELECT id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update FROM fear_greed_index -WHERE deleted_at IS NULL -ORDER BY timestamp DESC -LIMIT ? OFFSET ? -` - -type ListFearGreedIndexHistoryParams struct { - Limit int64 `json:"limit"` - Offset int64 `json:"offset"` -} - -func (q *Queries) ListFearGreedIndexHistory(ctx context.Context, arg ListFearGreedIndexHistoryParams) ([]FearGreedIndex, error) { - rows, err := q.db.QueryContext(ctx, listFearGreedIndexHistory, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []FearGreedIndex - for rows.Next() { - var i FearGreedIndex - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.Value, - &i.ValueClassification, - &i.Timestamp, - &i.TimeUntilUpdate, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listGlobalMarketHistory = `-- name: ListGlobalMarketHistory :many -SELECT id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated FROM global_market -WHERE deleted_at IS NULL -ORDER BY last_updated DESC -LIMIT ? OFFSET ? -` - -type ListGlobalMarketHistoryParams struct { - Limit int64 `json:"limit"` - Offset int64 `json:"offset"` -} - -func (q *Queries) ListGlobalMarketHistory(ctx context.Context, arg ListGlobalMarketHistoryParams) ([]GlobalMarket, error) { - rows, err := q.db.QueryContext(ctx, listGlobalMarketHistory, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GlobalMarket - for rows.Next() { - var i GlobalMarket - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.TotalMarketCapUsd, - &i.Total24hVolumeUsd, - &i.BitcoinPercentageOfMarketCap, - &i.ActiveCurrencies, - &i.ActiveAssets, - &i.ActiveMarkets, - &i.LastUpdated, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const listPriceHistoryByAssetID = `-- name: ListPriceHistoryByAssetID :many SELECT id, created_at, updated_at, deleted_at, asset_id, price_usd, price_btc, volume_24h_usd, market_cap_usd, available_supply, total_supply, max_supply, percent_change_1h, percent_change_24h, percent_change_7d, rank, last_updated FROM prices WHERE asset_id = ? AND deleted_at IS NULL @@ -2309,39 +1861,6 @@ func (q *Queries) SoftDeleteBlockchain(ctx context.Context, id string) error { return err } -const softDeleteCryptoListing = `-- name: SoftDeleteCryptoListing :exec -UPDATE crypto_listings -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ? -` - -func (q *Queries) SoftDeleteCryptoListing(ctx context.Context, id string) error { - _, err := q.db.ExecContext(ctx, softDeleteCryptoListing, id) - return err -} - -const softDeleteFearGreedIndex = `-- name: SoftDeleteFearGreedIndex :exec -UPDATE fear_greed_index -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ? -` - -func (q *Queries) SoftDeleteFearGreedIndex(ctx context.Context, id string) error { - _, err := q.db.ExecContext(ctx, softDeleteFearGreedIndex, id) - return err -} - -const softDeleteGlobalMarket = `-- name: SoftDeleteGlobalMarket :exec -UPDATE global_market -SET deleted_at = CURRENT_TIMESTAMP -WHERE id = ? -` - -func (q *Queries) SoftDeleteGlobalMarket(ctx context.Context, id string) error { - _, err := q.db.ExecContext(ctx, softDeleteGlobalMarket, id) - return err -} - const softDeletePriceConversion = `-- name: SoftDeletePriceConversion :exec UPDATE price_conversions SET deleted_at = CURRENT_TIMESTAMP @@ -2942,144 +2461,6 @@ func (q *Queries) UpdateBlockchainSocialLinks(ctx context.Context, arg UpdateBlo return i, err } -const updateCryptoListing = `-- name: UpdateCryptoListing :one -UPDATE crypto_listings -SET - name = ?, - symbol = ?, - website_slug = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING id, created_at, updated_at, deleted_at, api_id, name, symbol, website_slug -` - -type UpdateCryptoListingParams struct { - Name string `json:"name"` - Symbol string `json:"symbol"` - WebsiteSlug string `json:"website_slug"` - ID string `json:"id"` -} - -func (q *Queries) UpdateCryptoListing(ctx context.Context, arg UpdateCryptoListingParams) (CryptoListing, error) { - row := q.db.QueryRowContext(ctx, updateCryptoListing, - arg.Name, - arg.Symbol, - arg.WebsiteSlug, - arg.ID, - ) - var i CryptoListing - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.ApiID, - &i.Name, - &i.Symbol, - &i.WebsiteSlug, - ) - return i, err -} - -const updateFearGreedIndex = `-- name: UpdateFearGreedIndex :one -UPDATE fear_greed_index -SET - value = ?, - value_classification = ?, - timestamp = ?, - time_until_update = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING id, created_at, updated_at, deleted_at, value, value_classification, timestamp, time_until_update -` - -type UpdateFearGreedIndexParams struct { - Value int64 `json:"value"` - ValueClassification string `json:"value_classification"` - Timestamp time.Time `json:"timestamp"` - TimeUntilUpdate sql.NullString `json:"time_until_update"` - ID string `json:"id"` -} - -func (q *Queries) UpdateFearGreedIndex(ctx context.Context, arg UpdateFearGreedIndexParams) (FearGreedIndex, error) { - row := q.db.QueryRowContext(ctx, updateFearGreedIndex, - arg.Value, - arg.ValueClassification, - arg.Timestamp, - arg.TimeUntilUpdate, - arg.ID, - ) - var i FearGreedIndex - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.Value, - &i.ValueClassification, - &i.Timestamp, - &i.TimeUntilUpdate, - ) - return i, err -} - -const updateGlobalMarket = `-- name: UpdateGlobalMarket :one -UPDATE global_market -SET - total_market_cap_usd = ?, - total_24h_volume_usd = ?, - bitcoin_percentage_of_market_cap = ?, - active_currencies = ?, - active_assets = ?, - active_markets = ?, - last_updated = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -AND deleted_at IS NULL -RETURNING id, created_at, updated_at, deleted_at, total_market_cap_usd, total_24h_volume_usd, bitcoin_percentage_of_market_cap, active_currencies, active_assets, active_markets, last_updated -` - -type UpdateGlobalMarketParams struct { - TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"` - Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"` - BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"` - ActiveCurrencies sql.NullInt64 `json:"active_currencies"` - ActiveAssets sql.NullInt64 `json:"active_assets"` - ActiveMarkets sql.NullInt64 `json:"active_markets"` - LastUpdated time.Time `json:"last_updated"` - ID string `json:"id"` -} - -func (q *Queries) UpdateGlobalMarket(ctx context.Context, arg UpdateGlobalMarketParams) (GlobalMarket, error) { - row := q.db.QueryRowContext(ctx, updateGlobalMarket, - arg.TotalMarketCapUsd, - arg.Total24hVolumeUsd, - arg.BitcoinPercentageOfMarketCap, - arg.ActiveCurrencies, - arg.ActiveAssets, - arg.ActiveMarkets, - arg.LastUpdated, - arg.ID, - ) - var i GlobalMarket - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.DeletedAt, - &i.TotalMarketCapUsd, - &i.Total24hVolumeUsd, - &i.BitcoinPercentageOfMarketCap, - &i.ActiveCurrencies, - &i.ActiveAssets, - &i.ActiveMarkets, - &i.LastUpdated, - ) - return i, err -} - const updatePrice = `-- name: UpdatePrice :one UPDATE prices SET diff --git a/internal/sink/network/schema.sql b/internal/db/network/schema.sql similarity index 71% rename from internal/sink/network/schema.sql rename to internal/db/network/schema.sql index e2293cf..297f045 100644 --- a/internal/sink/network/schema.sql +++ b/internal/db/network/schema.sql @@ -52,46 +52,6 @@ CREATE TABLE price_conversions ( UNIQUE(price_id, currency_code) ); --- Global market data from Alternative.me API -CREATE TABLE global_market ( - id TEXT PRIMARY KEY, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP, - total_market_cap_usd REAL, - total_24h_volume_usd REAL, - bitcoin_percentage_of_market_cap REAL, - active_currencies INTEGER, - active_assets INTEGER, - active_markets INTEGER, - last_updated TIMESTAMP NOT NULL -); - --- Fear and Greed Index data from Alternative.me -CREATE TABLE fear_greed_index ( - id TEXT PRIMARY KEY, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP, - value INTEGER NOT NULL, - value_classification TEXT NOT NULL, - timestamp TIMESTAMP NOT NULL, - time_until_update TEXT -); - --- Listings data from Alternative.me API -CREATE TABLE crypto_listings ( - id TEXT PRIMARY KEY, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP, - api_id TEXT NOT NULL, - name TEXT NOT NULL, - symbol TEXT NOT NULL, - website_slug TEXT NOT NULL, - UNIQUE(api_id) -); - -- Blockchains table to store chain configuration parameters CREATE TABLE blockchains ( id TEXT PRIMARY KEY, @@ -172,18 +132,6 @@ CREATE INDEX idx_price_conversions_price_id ON price_conversions(price_id); CREATE INDEX idx_price_conversions_currency_code ON price_conversions(currency_code); CREATE INDEX idx_price_conversions_deleted_at ON price_conversions(deleted_at); -CREATE INDEX idx_global_market_last_updated ON global_market(last_updated); -CREATE INDEX idx_global_market_deleted_at ON global_market(deleted_at); - -CREATE INDEX idx_fear_greed_index_timestamp ON fear_greed_index(timestamp); -CREATE INDEX idx_fear_greed_index_value ON fear_greed_index(value); -CREATE INDEX idx_fear_greed_index_deleted_at ON fear_greed_index(deleted_at); - -CREATE INDEX idx_crypto_listings_api_id ON crypto_listings(api_id); -CREATE INDEX idx_crypto_listings_symbol ON crypto_listings(symbol); -CREATE INDEX idx_crypto_listings_website_slug ON crypto_listings(website_slug); -CREATE INDEX idx_crypto_listings_deleted_at ON crypto_listings(deleted_at); - CREATE INDEX idx_blockchains_chain_name ON blockchains(chain_name); CREATE INDEX idx_blockchains_chain_id_cosmos ON blockchains(chain_id_cosmos); CREATE INDEX idx_blockchains_chain_id_evm ON blockchains(chain_id_evm); diff --git a/internal/sink/sqlc.yaml b/internal/db/sqlc.yaml similarity index 100% rename from internal/sink/sqlc.yaml rename to internal/db/sqlc.yaml diff --git a/internal/sink/users/db.go b/internal/db/users/db.go similarity index 100% rename from internal/sink/users/db.go rename to internal/db/users/db.go diff --git a/internal/sink/users/models.go b/internal/db/users/models.go similarity index 100% rename from internal/sink/users/models.go rename to internal/db/users/models.go diff --git a/internal/sink/users/querier.go b/internal/db/users/querier.go similarity index 100% rename from internal/sink/users/querier.go rename to internal/db/users/querier.go diff --git a/internal/sink/users/query.sql b/internal/db/users/query.sql similarity index 100% rename from internal/sink/users/query.sql rename to internal/db/users/query.sql diff --git a/internal/sink/users/query.sql.go b/internal/db/users/query.sql.go similarity index 100% rename from internal/sink/users/query.sql.go rename to internal/db/users/query.sql.go diff --git a/internal/sink/users/schema.sql b/internal/db/users/schema.sql similarity index 100% rename from internal/sink/users/schema.sql rename to internal/db/users/schema.sql diff --git a/internal/jobs/cron.go b/internal/jobs/cron.go new file mode 100644 index 0000000..415ac45 --- /dev/null +++ b/internal/jobs/cron.go @@ -0,0 +1 @@ +package jobs diff --git a/internal/jobs/events.go b/internal/jobs/events.go new file mode 100644 index 0000000..fb9b182 --- /dev/null +++ b/internal/jobs/events.go @@ -0,0 +1,12 @@ +package jobs + +type EventTrigger string + +var ( + EventTriggerMinute = EventTrigger("0 * * * * *") // Every minute (with seconds) + EventTriggerHourly = EventTrigger("0 0 * * * *") // Every hour at minute 0 + EventTriggerDaily = EventTrigger("0 0 0 * * *") // Every day at 00:00:00 + EventTriggerWeekly = EventTrigger("0 0 0 * * 0") // Every Sunday at 00:00:00 + EventTriggerMonthly = EventTrigger("0 0 0 1 * *") // First day of every month at 00:00:00 + EventTriggerYearly = EventTrigger("0 0 0 1 1 *") // January 1st every year at 00:00:00 +) diff --git a/internal/jobs/tasks.go b/internal/jobs/tasks.go new file mode 100644 index 0000000..415ac45 --- /dev/null +++ b/internal/jobs/tasks.go @@ -0,0 +1 @@ +package jobs diff --git a/internal/migrate/001_accounts_table.down.sql b/internal/migrate/001_accounts_table.down.sql new file mode 100644 index 0000000..5eb8e05 --- /dev/null +++ b/internal/migrate/001_accounts_table.down.sql @@ -0,0 +1 @@ +DROP TABLE accounts; diff --git a/internal/migrate/001_accounts_table.up.sql b/internal/migrate/001_accounts_table.up.sql new file mode 100644 index 0000000..fe917a8 --- /dev/null +++ b/internal/migrate/001_accounts_table.up.sql @@ -0,0 +1,30 @@ +-- Accounts represent blockchain accounts +CREATE TABLE accounts ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + number INTEGER NOT NULL, + sequence INTEGER NOT NULL DEFAULT 0, + address TEXT NOT NULL UNIQUE, + public_key TEXT NOT NULL CHECK(json_valid(public_key)), + chain_id TEXT NOT NULL, + block_created INTEGER NOT NULL, + controller TEXT NOT NULL, + label TEXT NOT NULL, + handle TEXT NOT NULL, + is_subsidiary BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_subsidiary IN (0,1)), + is_validator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_validator IN (0,1)), + is_delegator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegator IN (0,1)), + is_accountable BOOLEAN NOT NULL DEFAULT TRUE CHECK(is_accountable IN (0,1)) +); + + +CREATE INDEX idx_accounts_address ON accounts(address); +CREATE INDEX idx_accounts_chain_id ON accounts(chain_id); +CREATE INDEX idx_accounts_block_created ON accounts(block_created); +CREATE INDEX idx_accounts_label ON accounts(label); +CREATE INDEX idx_accounts_controller ON accounts(controller); +CREATE INDEX idx_accounts_deleted_at ON accounts(deleted_at); + + diff --git a/internal/migrate/002_credentials_table.down.sql b/internal/migrate/002_credentials_table.down.sql new file mode 100644 index 0000000..ff412a9 --- /dev/null +++ b/internal/migrate/002_credentials_table.down.sql @@ -0,0 +1 @@ +DROP TABLE credential; diff --git a/internal/migrate/002_credentials_table.up.sql b/internal/migrate/002_credentials_table.up.sql new file mode 100644 index 0000000..747f3ff --- /dev/null +++ b/internal/migrate/002_credentials_table.up.sql @@ -0,0 +1,19 @@ +-- Credentials store WebAuthn credentials +CREATE TABLE credentials ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + handle TEXT NOT NULL, + credential_id TEXT NOT NULL UNIQUE, + authenticator_attachment TEXT NOT NULL, + origin TEXT NOT NULL, + type TEXT NOT NULL, + transports TEXT NOT NULL +); + +CREATE INDEX idx_credentials_handle ON credentials(handle); +CREATE INDEX idx_credentials_origin ON credentials(origin); +CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at); + + diff --git a/internal/migrate/003_profiles_table.down.sql b/internal/migrate/003_profiles_table.down.sql new file mode 100644 index 0000000..f23f48a --- /dev/null +++ b/internal/migrate/003_profiles_table.down.sql @@ -0,0 +1 @@ +DROP TABLE profiles; diff --git a/internal/migrate/003_profiles_table.up.sql b/internal/migrate/003_profiles_table.up.sql new file mode 100644 index 0000000..df0dc16 --- /dev/null +++ b/internal/migrate/003_profiles_table.up.sql @@ -0,0 +1,18 @@ +-- Profiles represent user identities +CREATE TABLE profiles ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + address TEXT NOT NULL, + handle TEXT NOT NULL UNIQUE, + origin TEXT NOT NULL, + name TEXT NOT NULL, + UNIQUE(address, origin) +); + +CREATE INDEX idx_profiles_handle ON profiles(handle); +CREATE INDEX idx_profiles_address ON profiles(address); +CREATE INDEX idx_profiles_deleted_at ON profiles(deleted_at); + + diff --git a/internal/migrate/004_vaults_table.down.sql b/internal/migrate/004_vaults_table.down.sql new file mode 100644 index 0000000..b233be1 --- /dev/null +++ b/internal/migrate/004_vaults_table.down.sql @@ -0,0 +1 @@ +DROP TABLE vaults; diff --git a/internal/migrate/004_vaults_table.up.sql b/internal/migrate/004_vaults_table.up.sql new file mode 100644 index 0000000..4f4a505 --- /dev/null +++ b/internal/migrate/004_vaults_table.up.sql @@ -0,0 +1,19 @@ +-- Vaults store encrypted data +CREATE TABLE vaults ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + handle TEXT NOT NULL, + origin TEXT NOT NULL, + address TEXT NOT NULL, + cid TEXT NOT NULL UNIQUE, + config TEXT NOT NULL CHECK(json_valid(config)), + session_id TEXT NOT NULL, + redirect_uri TEXT NOT NULL +); + + +CREATE INDEX idx_vaults_handle ON vaults(handle); +CREATE INDEX idx_vaults_session_id ON vaults(session_id); +CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at) diff --git a/internal/migrate/005_assets_table.down.sql b/internal/migrate/005_assets_table.down.sql new file mode 100644 index 0000000..a2988f5 --- /dev/null +++ b/internal/migrate/005_assets_table.down.sql @@ -0,0 +1 @@ +DROP TABLE assets; diff --git a/internal/migrate/005_assets_table.up.sql b/internal/migrate/005_assets_table.up.sql new file mode 100644 index 0000000..2dfa1f1 --- /dev/null +++ b/internal/migrate/005_assets_table.up.sql @@ -0,0 +1,21 @@ +-- Assets represent tokens and coins +CREATE TABLE assets ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + name TEXT NOT NULL, + symbol TEXT NOT NULL, + decimals INTEGER NOT NULL CHECK(decimals >= 0), + chain_id TEXT NOT NULL, + channel TEXT NOT NULL, + asset_type TEXT NOT NULL, + coingecko_id TEXT, + UNIQUE(chain_id, symbol) +); + +CREATE INDEX idx_assets_symbol ON assets(symbol); +CREATE INDEX idx_assets_chain_id ON assets(chain_id); +CREATE INDEX idx_assets_deleted_at ON assets(deleted_at); + + diff --git a/internal/migrate/006_prices_table.down.sql b/internal/migrate/006_prices_table.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/internal/migrate/006_prices_table.up.sql b/internal/migrate/006_prices_table.up.sql new file mode 100644 index 0000000..9333756 --- /dev/null +++ b/internal/migrate/006_prices_table.up.sql @@ -0,0 +1,28 @@ +-- Prices entity based on the Alternative.me API for crypto prices +CREATE TABLE prices ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + asset_id TEXT NOT NULL, + price_usd REAL, + price_btc REAL, + volume_24h_usd REAL, + market_cap_usd REAL, + available_supply REAL, + total_supply REAL, + max_supply REAL, + percent_change_1h REAL, + percent_change_24h REAL, + percent_change_7d REAL, + rank INTEGER, + last_updated TIMESTAMP NOT NULL, + FOREIGN KEY (asset_id) REFERENCES assets(id) +); + +CREATE INDEX idx_prices_asset_id ON prices(asset_id); +CREATE INDEX idx_prices_rank ON prices(rank); +CREATE INDEX idx_prices_last_updated ON prices(last_updated); +CREATE INDEX idx_prices_deleted_at ON prices(deleted_at); + + diff --git a/internal/migrate/007_price_conversions_table.down.sql b/internal/migrate/007_price_conversions_table.down.sql new file mode 100644 index 0000000..926f7d5 --- /dev/null +++ b/internal/migrate/007_price_conversions_table.down.sql @@ -0,0 +1 @@ +DROP TABLE price_conversions; diff --git a/internal/migrate/007_price_conversions_table.up.sql b/internal/migrate/007_price_conversions_table.up.sql new file mode 100644 index 0000000..3310f1b --- /dev/null +++ b/internal/migrate/007_price_conversions_table.up.sql @@ -0,0 +1,21 @@ +-- Currency conversion rates for crypto prices +CREATE TABLE price_conversions ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + price_id TEXT NOT NULL, + currency_code TEXT NOT NULL, + price REAL, + volume_24h REAL, + market_cap REAL, + last_updated TIMESTAMP NOT NULL, + FOREIGN KEY (price_id) REFERENCES prices(id), + UNIQUE(price_id, currency_code) +); + +CREATE INDEX idx_price_conversions_price_id ON price_conversions(price_id); +CREATE INDEX idx_price_conversions_currency_code ON price_conversions(currency_code); +CREATE INDEX idx_price_conversions_deleted_at ON price_conversions(deleted_at); + + diff --git a/internal/migrate/008_blockchains_table.down.sql b/internal/migrate/008_blockchains_table.down.sql new file mode 100644 index 0000000..922ed95 --- /dev/null +++ b/internal/migrate/008_blockchains_table.down.sql @@ -0,0 +1 @@ +DROP TABLE blockchains; diff --git a/internal/migrate/008_blockchains_table.up.sql b/internal/migrate/008_blockchains_table.up.sql new file mode 100644 index 0000000..0e58a5c --- /dev/null +++ b/internal/migrate/008_blockchains_table.up.sql @@ -0,0 +1,71 @@ +-- Blockchains table to store chain configuration parameters +CREATE TABLE blockchains ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + + -- Basic chain information + chain_name TEXT NOT NULL, + chain_id_cosmos TEXT, + chain_id_evm TEXT, + api_name TEXT, + bech_account_prefix TEXT, + bech_validator_prefix TEXT, + + -- Chain assets + main_asset_symbol TEXT, + main_asset_denom TEXT, + staking_asset_symbol TEXT, + staking_asset_denom TEXT, + is_stake_enabled BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_stake_enabled IN (0,1)), + + -- Chain images + chain_image TEXT, + main_asset_image TEXT, + staking_asset_image TEXT, + + -- Chain types and features + chain_type TEXT NOT NULL CHECK(json_valid(chain_type)), + is_support_mobile_wallet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_mobile_wallet IN (0,1)), + is_support_extension_wallet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_extension_wallet IN (0,1)), + is_support_erc20 BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_erc20 IN (0,1)), + + -- Descriptions in multiple languages + description_en TEXT, + description_ko TEXT, + description_ja TEXT, + + -- Genesis information + origin_genesis_time TIMESTAMP, + + -- Account types configuration + account_type TEXT NOT NULL CHECK(json_valid(account_type)), + + -- BTC staking specific + btc_staking TEXT CHECK(json_valid(btc_staking)), + + -- Cosmos fee information + cosmos_fee_info TEXT CHECK(json_valid(cosmos_fee_info)), + + -- EVM fee information + evm_fee_info TEXT CHECK(json_valid(evm_fee_info)), + + -- Endpoints + lcd_endpoint TEXT CHECK(json_valid(lcd_endpoint)), + grpc_endpoint TEXT CHECK(json_valid(grpc_endpoint)), + evm_rpc_endpoint TEXT CHECK(json_valid(evm_rpc_endpoint)), + + -- Explorer information + explorer TEXT CHECK(json_valid(explorer)), + + -- Social and documentation links + about TEXT CHECK(json_valid(about)), + forum TEXT CHECK(json_valid(forum)) +); + +CREATE INDEX idx_blockchains_chain_name ON blockchains(chain_name); +CREATE INDEX idx_blockchains_chain_id_cosmos ON blockchains(chain_id_cosmos); +CREATE INDEX idx_blockchains_chain_id_evm ON blockchains(chain_id_evm); +CREATE INDEX idx_blockchains_main_asset_symbol ON blockchains(main_asset_symbol); +CREATE INDEX idx_blockchains_deleted_at ON blockchains(deleted_at); diff --git a/internal/migrate/009_services_table.down.sql b/internal/migrate/009_services_table.down.sql new file mode 100644 index 0000000..4dd8073 --- /dev/null +++ b/internal/migrate/009_services_table.down.sql @@ -0,0 +1 @@ +DROP TABLE services; diff --git a/internal/migrate/009_services_table.up.sql b/internal/migrate/009_services_table.up.sql new file mode 100644 index 0000000..459f653 --- /dev/null +++ b/internal/migrate/009_services_table.up.sql @@ -0,0 +1,24 @@ +-- Service for Service Records sourced on chain +CREATE TABLE services ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + name TEXT NOT NULL, + description TEXT, + chain_id TEXT NOT NULL, + address TEXT NOT NULL, + owner_address TEXT NOT NULL, + metadata TEXT CHECK(json_valid(metadata)), + status TEXT NOT NULL, + block_height INTEGER NOT NULL, + FOREIGN KEY (chain_id) REFERENCES assets(chain_id), + UNIQUE(chain_id, address) +); + +CREATE INDEX idx_services_name ON services(name); +CREATE INDEX idx_services_chain_id ON services(chain_id); +CREATE INDEX idx_services_address ON services(address); +CREATE INDEX idx_services_owner_address ON services(owner_address); +CREATE INDEX idx_services_status ON services(status); +CREATE INDEX idx_services_deleted_at ON services(deleted_at); diff --git a/internal/migrate/010_activities_table.down.sql b/internal/migrate/010_activities_table.down.sql new file mode 100644 index 0000000..c0537ec --- /dev/null +++ b/internal/migrate/010_activities_table.down.sql @@ -0,0 +1 @@ +DROP TABLE activities; diff --git a/internal/migrate/010_activities_table.up.sql b/internal/migrate/010_activities_table.up.sql new file mode 100644 index 0000000..50d5757 --- /dev/null +++ b/internal/migrate/010_activities_table.up.sql @@ -0,0 +1,32 @@ + +-- Activity table for basic transaction broadcast activity +CREATE TABLE activities ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + account_id TEXT NOT NULL, + tx_hash TEXT, + tx_type TEXT NOT NULL, + status TEXT NOT NULL, + amount TEXT, + fee TEXT, + gas_used INTEGER, + gas_wanted INTEGER, + memo TEXT, + block_height INTEGER, + timestamp TIMESTAMP NOT NULL, + raw_log TEXT, + error TEXT, + FOREIGN KEY (account_id) REFERENCES accounts(id) +); + +CREATE INDEX idx_activities_account_id ON activities(account_id); +CREATE INDEX idx_activities_tx_hash ON activities(tx_hash); +CREATE INDEX idx_activities_tx_type ON activities(tx_type); +CREATE INDEX idx_activities_status ON activities(status); +CREATE INDEX idx_activities_timestamp ON activities(timestamp); +CREATE INDEX idx_activities_block_height ON activities(block_height); +CREATE INDEX idx_activities_deleted_at ON activities(deleted_at); + + diff --git a/internal/migrate/011_health_table.down.sql b/internal/migrate/011_health_table.down.sql new file mode 100644 index 0000000..50d5757 --- /dev/null +++ b/internal/migrate/011_health_table.down.sql @@ -0,0 +1,32 @@ + +-- Activity table for basic transaction broadcast activity +CREATE TABLE activities ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + account_id TEXT NOT NULL, + tx_hash TEXT, + tx_type TEXT NOT NULL, + status TEXT NOT NULL, + amount TEXT, + fee TEXT, + gas_used INTEGER, + gas_wanted INTEGER, + memo TEXT, + block_height INTEGER, + timestamp TIMESTAMP NOT NULL, + raw_log TEXT, + error TEXT, + FOREIGN KEY (account_id) REFERENCES accounts(id) +); + +CREATE INDEX idx_activities_account_id ON activities(account_id); +CREATE INDEX idx_activities_tx_hash ON activities(tx_hash); +CREATE INDEX idx_activities_tx_type ON activities(tx_type); +CREATE INDEX idx_activities_status ON activities(status); +CREATE INDEX idx_activities_timestamp ON activities(timestamp); +CREATE INDEX idx_activities_block_height ON activities(block_height); +CREATE INDEX idx_activities_deleted_at ON activities(deleted_at); + + diff --git a/internal/migrate/011_health_table.up.sql b/internal/migrate/011_health_table.up.sql new file mode 100644 index 0000000..47b30ce --- /dev/null +++ b/internal/migrate/011_health_table.up.sql @@ -0,0 +1,28 @@ +-- Health table for scheduled checks for API endpoints +CREATE TABLE health ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + endpoint_url TEXT NOT NULL, + endpoint_type TEXT NOT NULL, + chain_id TEXT, + status TEXT NOT NULL, + response_time_ms INTEGER, + last_checked TIMESTAMP NOT NULL, + next_check TIMESTAMP, + failure_count INTEGER NOT NULL DEFAULT 0, + success_count INTEGER NOT NULL DEFAULT 0, + response_data TEXT, + error_message TEXT, + FOREIGN KEY (chain_id) REFERENCES assets(chain_id) +); + +CREATE INDEX idx_health_endpoint_url ON health(endpoint_url); +CREATE INDEX idx_health_endpoint_type ON health(endpoint_type); +CREATE INDEX idx_health_chain_id ON health(chain_id); +CREATE INDEX idx_health_status ON health(status); +CREATE INDEX idx_health_last_checked ON health(last_checked); +CREATE INDEX idx_health_next_check ON health(next_check); +CREATE INDEX idx_health_deleted_at ON health(deleted_at); + diff --git a/internal/migrate/012_global_market_table.down.sql b/internal/migrate/012_global_market_table.down.sql new file mode 100644 index 0000000..04d1d4b --- /dev/null +++ b/internal/migrate/012_global_market_table.down.sql @@ -0,0 +1 @@ +DROP TABLE global_market; diff --git a/internal/migrate/012_global_market_table.up.sql b/internal/migrate/012_global_market_table.up.sql new file mode 100644 index 0000000..8216c4b --- /dev/null +++ b/internal/migrate/012_global_market_table.up.sql @@ -0,0 +1,19 @@ +-- Global market data from Alternative.me API +CREATE TABLE global_market ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + total_market_cap_usd REAL, + total_24h_volume_usd REAL, + bitcoin_percentage_of_market_cap REAL, + active_currencies INTEGER, + active_assets INTEGER, + active_markets INTEGER, + last_updated TIMESTAMP NOT NULL +); + +CREATE INDEX idx_global_market_last_updated ON global_market(last_updated); +CREATE INDEX idx_global_market_deleted_at ON global_market(deleted_at); + + diff --git a/internal/migrate/013_fear_greed_index_table.down.sql b/internal/migrate/013_fear_greed_index_table.down.sql new file mode 100644 index 0000000..0eedee7 --- /dev/null +++ b/internal/migrate/013_fear_greed_index_table.down.sql @@ -0,0 +1 @@ +DROP TABLE fear_greed_index; diff --git a/internal/migrate/013_fear_greed_index_table.up.sql b/internal/migrate/013_fear_greed_index_table.up.sql new file mode 100644 index 0000000..8db8eef --- /dev/null +++ b/internal/migrate/013_fear_greed_index_table.up.sql @@ -0,0 +1,15 @@ +-- Fear and Greed Index data from Alternative.me +CREATE TABLE fear_greed_index ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + value INTEGER NOT NULL, + value_classification TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL, + time_until_update TEXT +); + +CREATE INDEX idx_fear_greed_index_timestamp ON fear_greed_index(timestamp); +CREATE INDEX idx_fear_greed_index_value ON fear_greed_index(value); +CREATE INDEX idx_fear_greed_index_deleted_at ON fear_greed_index(deleted_at); diff --git a/internal/migrate/014_crypto_listings_table.down.sql b/internal/migrate/014_crypto_listings_table.down.sql new file mode 100644 index 0000000..5dc3abc --- /dev/null +++ b/internal/migrate/014_crypto_listings_table.down.sql @@ -0,0 +1 @@ +DROP TABLE crypto_listings; diff --git a/internal/migrate/014_crypto_listings_table.up.sql b/internal/migrate/014_crypto_listings_table.up.sql new file mode 100644 index 0000000..f190d07 --- /dev/null +++ b/internal/migrate/014_crypto_listings_table.up.sql @@ -0,0 +1,18 @@ +-- Listings data from Alternative.me API +CREATE TABLE crypto_listings ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + api_id TEXT NOT NULL, + name TEXT NOT NULL, + symbol TEXT NOT NULL, + website_slug TEXT NOT NULL, + UNIQUE(api_id) +); + + +CREATE INDEX idx_crypto_listings_api_id ON crypto_listings(api_id); +CREATE INDEX idx_crypto_listings_symbol ON crypto_listings(symbol); +CREATE INDEX idx_crypto_listings_website_slug ON crypto_listings(website_slug); +CREATE INDEX idx_crypto_listings_deleted_at ON crypto_listings(deleted_at); diff --git a/internal/migrate/Taskfile.yml b/internal/migrate/Taskfile.yml new file mode 100644 index 0000000..0280ae6 --- /dev/null +++ b/internal/migrate/Taskfile.yml @@ -0,0 +1,199 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json +version: "3" +silent: true + +tasks: + default: + cmd: task -l --json | jq -r '.tasks[].name' | fzf | xargs task + + initialize: + cmds: + - task: migrate:accounts:up + - task: migrate:credentials:up + - task: migrate:profiles:up + - task: migrate:vaults:up + - task: migrate:assets:up + - task: migrate:prices:up + - task: migrate:price_conversions:up + - task: migrate:blockchains:up + - task: migrate:services:up + - task: migrate:activities:up + - task: migrate:health:up + - task: migrate:global_market:up + - task: migrate:fear_greed_index:up + - task: migrate:crypto_listings:up + + migrate: + cmds: + - task: migrate:accounts + - task: migrate:credentials + - task: migrate:profiles + - task: migrate:vaults + - task: migrate:assets + - task: migrate:prices + - task: migrate:price_conversions + - task: migrate:blockchains + - task: migrate:services + - task: migrate:activities + - task: migrate:health + - task: migrate:global_market + - task: migrate:fear_greed_index + - task: migrate:crypto_listings + + # --------------- + # Main Tasks + # --------------- + migrate:accounts: + cmds: + - task: migrate:accounts:down + - task: migrate:accounts:up + + migrate:accounts:up: + cmd: wrangler d1 execute USERS_DB --file 001_accounts_table.up.sql --remote + + migrate:accounts:down: + cmd: wrangler d1 execute USERS_DB --file 001_accounts_table.down.sql --remote + + migrate:credentials: + cmds: + - task: migrate:credentials:down + - task: migrate:credentials:up + + migrate:credentials:up: + cmd: wrangler d1 execute USERS_DB --file 002_credentials_table.up.sql --remote + + migrate:credentials:down: + cmd: wrangler d1 execute USERS_DB --file 002_credentials_table.down.sql --remote + + migrate:profiles: + cmds: + - task: migrate:profiles:down + - task: migrate:profiles:up + + migrate:profiles:up: + cmd: wrangler d1 execute USERS_DB --file 003_profiles_table.up.sql --remote + + migrate:profiles:down: + cmd: wrangler d1 execute USERS_DB --file 003_profiles_table.down.sql --remote + + migrate:vaults: + cmds: + - task: migrate:vaults:down + - task: migrate:vaults:up + + migrate:vaults:down: + cmd: wrangler d1 execute USERS_DB --file 004_vaults_table.down.sql --remote + + migrate:vaults:up: + cmd: wrangler d1 execute USERS_DB --file 004_vaults_table.up.sql --remote + + migrate:assets: + cmds: + - task: migrate:assets:down + - task: migrate:assets:up + + migrate:assets:up: + cmd: wrangler d1 execute NETWORK_DB --file 005_assets_table.up.sql --remote + + migrate:assets:down: + cmd: wrangler d1 execute NETWORK_DB --file 005_assets_table.down.sql --remote + + migrate:prices: + cmds: + - task: migrate:prices:down + - task: migrate:prices:up + + migrate:prices:up: + cmd: wrangler d1 execute NETWORK_DB --file 006_prices_table.up.sql --remote + + migrate:prices:down: + cmd: wrangler d1 execute NETWORK_DB --file 006_prices_table.down.sql --remote + + migrate:price_conversions: + cmds: + - task: migrate:price_conversions:down + - task: migrate:price_conversions:up + + migrate:price_conversions:up: + cmd: wrangler d1 execute NETWORK_DB --file 007_price_conversions_table.up.sql --remote + + migrate:price_conversions:down: + cmd: wrangler d1 execute NETWORK_DB --file 007_price_conversions_table.down.sql --remote + + migrate:blockchains: + cmds: + - task: migrate:blockchains:down + - task: migrate:blockchains:up + + migrate:blockchains:up: + cmd: wrangler d1 execute NETWORK_DB --file 008_blockchains_table.up.sql --remote + + migrate:blockchains:down: + cmd: wrangler d1 execute NETWORK_DB --file 008_blockchains_table.down.sql --remote + + migrate:services: + cmds: + - task: migrate:services:down + - task: migrate:services:up + + migrate:services:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 009_services_table.up.sql --remote + + migrate:services:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 009_services_table.down.sql --remote + + migrate:activities: + cmds: + - task: migrate:activities:down + - task: migrate:activities:up + + migrate:activities:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 010_activities_table.up.sql --remote + + migrate:activities:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 010_activities_table.down.sql --remote + + migrate:health: + cmds: + - task: migrate:health:down + - task: migrate:health:up + + migrate:health:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 011_health_table.up.sql --remote + + migrate:health:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 011_health_table.down.sql --remote + + migrate:global_market: + cmds: + - task: global_market:down + - task: global_market:up + + migrate:global_market:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 012_global_market_table.up.sql --remote + + migrate:global_market:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 012_global_market_table.down.sql --remote + + migrate:fear_greed_index: + cmds: + - task: migrate:fear_greed_index:down + - task: migrate:fear_greed_index:up + + migrate:fear_greed_index:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 013_fear_greed_index_table.up.sql --remote + + migrate:fear_greed_index:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 013_fear_greed_index_table.down.sql --remote + + migrate:crypto_listings: + cmds: + - task: migrate:crypto_listings:down + - task: migrate:crypto_listings:up + + migrate:crypto_listings:up: + cmd: wrangler d1 execute ACTIVITY_DB --file 014_crypto_listings_table.up.sql --remote + + migrate:crypto_listings:down: + cmd: wrangler d1 execute ACTIVITY_DB --file 014_crypto_listings_table.down.sql --remote + diff --git a/internal/migrate/wrangler.toml b/internal/migrate/wrangler.toml new file mode 100644 index 0000000..b898e6a --- /dev/null +++ b/internal/migrate/wrangler.toml @@ -0,0 +1,64 @@ +# Top-level configuration +name = "motr-worker" +main = "worker.mjs" +compatibility_date = "2025-04-14" + +routes = [ + { pattern = "sonr.id", custom_domain = true }, +] + +[build] +command = "devbox run build:worker" + +[dev] +port = 6969 + +[observability] +enabled = true + +[triggers] +crons = ["0 */1 * * *"] + +[[d1_databases]] +binding = "ACTIVITY_DB" +database_name = "motr-activity" +database_id = "a7ccb4bb-c529-4f42-8029-92564a3aecb8" + +[[d1_databases]] +binding = "NETWORK_DB" +database_name = "motr-network" +database_id = "acb75499-3502-4052-9604-263a913e077a" + +[[d1_databases]] +binding = "USERS_DB" +database_name = "motr-users" +database_id = "8ed4d399-5932-419c-b92f-9c20d7a36ad2" + +[[kv_namespaces]] +binding = "SESSIONS_KV" +id = "ea5de66fcfc14b5eba170395e29432ee" + +[[kv_namespaces]] +binding = "HANDLES_KV" +id = "271d47087a8842b2aac5ee79cf7bb203" + +[[r2_buckets]] +binding = 'PROFILES' +bucket_name = 'profiles' + +[vars] +SONR_CHAIN_ID = 'sonr-testnet-1' +IPFS_GATEWAY = 'https://ipfs.sonr.land' +SONR_API_URL = 'https://api.sonr.land' +SONR_RPC_URL = 'https://rpc.sonr.land' +SONR_GRPC_URL = 'https://grpc.sonr.land' +MATRIX_SERVER = 'https://bm.chat' +MOTR_GATEWAY = 'https://sonr.id' +MOTR_VAULT = 'https://did.run' + +[durable_objects] +bindings = [{name = "VAULT", class_name = "Vault"}] + +[[migrations]] +tag = "v1" # Should be unique for each entry +new_classes = ["Vault"] # List the classes that should be created diff --git a/internal/ui/charts/area_chart.templ b/internal/ui/dash/charts/area_chart.templ similarity index 100% rename from internal/ui/charts/area_chart.templ rename to internal/ui/dash/charts/area_chart.templ diff --git a/internal/ui/charts/area_chart_templ.go b/internal/ui/dash/charts/area_chart_templ.go similarity index 100% rename from internal/ui/charts/area_chart_templ.go rename to internal/ui/dash/charts/area_chart_templ.go diff --git a/internal/ui/charts/bar_chart.templ b/internal/ui/dash/charts/bar_chart.templ similarity index 100% rename from internal/ui/charts/bar_chart.templ rename to internal/ui/dash/charts/bar_chart.templ diff --git a/internal/ui/charts/bar_chart_templ.go b/internal/ui/dash/charts/bar_chart_templ.go similarity index 100% rename from internal/ui/charts/bar_chart_templ.go rename to internal/ui/dash/charts/bar_chart_templ.go diff --git a/internal/ui/charts/candle_chart.templ b/internal/ui/dash/charts/candle_chart.templ similarity index 100% rename from internal/ui/charts/candle_chart.templ rename to internal/ui/dash/charts/candle_chart.templ diff --git a/internal/ui/charts/candle_chart_templ.go b/internal/ui/dash/charts/candle_chart_templ.go similarity index 100% rename from internal/ui/charts/candle_chart_templ.go rename to internal/ui/dash/charts/candle_chart_templ.go diff --git a/internal/ui/charts/line_chart.templ b/internal/ui/dash/charts/line_chart.templ similarity index 100% rename from internal/ui/charts/line_chart.templ rename to internal/ui/dash/charts/line_chart.templ diff --git a/internal/ui/charts/line_chart_templ.go b/internal/ui/dash/charts/line_chart_templ.go similarity index 100% rename from internal/ui/charts/line_chart_templ.go rename to internal/ui/dash/charts/line_chart_templ.go diff --git a/internal/ui/charts/pie_chart.templ b/internal/ui/dash/charts/pie_chart.templ similarity index 100% rename from internal/ui/charts/pie_chart.templ rename to internal/ui/dash/charts/pie_chart.templ diff --git a/internal/ui/charts/pie_chart_templ.go b/internal/ui/dash/charts/pie_chart_templ.go similarity index 100% rename from internal/ui/charts/pie_chart_templ.go rename to internal/ui/dash/charts/pie_chart_templ.go diff --git a/internal/ui/dash/view.templ b/internal/ui/dash/view.templ index d3aef0e..a431eea 100644 --- a/internal/ui/dash/view.templ +++ b/internal/ui/dash/view.templ @@ -2,7 +2,7 @@ package dash import ( "github.com/sonr-io/motr/internal/ui" - "github.com/sonr-io/motr/pkg/session" + "github.com/sonr-io/motr/middleware/session" "time" ) diff --git a/internal/ui/home/view.templ b/internal/ui/home/view.templ index eb6a5f6..77cd99a 100644 --- a/internal/ui/home/view.templ +++ b/internal/ui/home/view.templ @@ -1,7 +1,7 @@ package home import "github.com/sonr-io/motr/internal/ui" -import "github.com/sonr-io/motr/pkg/session" +import "github.com/sonr-io/motr/middleware/session" func HomeView() templ.Component { return homeComponent() diff --git a/internal/ui/login/view.templ b/internal/ui/login/view.templ index 6b816d7..f5eb93b 100644 --- a/internal/ui/login/view.templ +++ b/internal/ui/login/view.templ @@ -1,13 +1,12 @@ package login import "github.com/sonr-io/motr/internal/ui" -import "github.com/sonr-io/motr/pkg/session" +import "github.com/sonr-io/motr/middleware/session" templ LoginView() { @ui.HTML() { @ui.Head() { @session.DefaultMetaComponent() - @ui.DefaultMetaComponent() } @ui.Nav() { @ui.NavLeft() { diff --git a/internal/ui/register/view.templ b/internal/ui/register/view.templ index c8b7b7e..ed4f843 100644 --- a/internal/ui/register/view.templ +++ b/internal/ui/register/view.templ @@ -1,13 +1,12 @@ package register import "github.com/sonr-io/motr/internal/ui" -import "github.com/sonr-io/motr/pkg/session" +import "github.com/sonr-io/motr/middleware/session" templ RegisterView() { @ui.HTML() { @ui.Head() { @session.DefaultMetaComponent() - @ui.DefaultMetaComponent() } @ui.Nav() { @ui.NavLeft() { diff --git a/pkg/cookies/cookies.go b/middleware/cookies/cookies.go similarity index 100% rename from pkg/cookies/cookies.go rename to middleware/cookies/cookies.go diff --git a/pkg/database/bindings.go b/middleware/database/bindings.go similarity index 79% rename from pkg/database/bindings.go rename to middleware/database/bindings.go index eac1fe0..55be09c 100644 --- a/pkg/database/bindings.go +++ b/middleware/database/bindings.go @@ -6,9 +6,9 @@ package database import ( "database/sql" - "github.com/sonr-io/motr/internal/sink/activity" - "github.com/sonr-io/motr/internal/sink/network" - "github.com/sonr-io/motr/internal/sink/users" + "github.com/sonr-io/motr/internal/db/activity" + "github.com/sonr-io/motr/internal/db/network" + "github.com/sonr-io/motr/internal/db/users" _ "github.com/syumai/workers/cloudflare/d1" ) diff --git a/pkg/database/connection.go b/middleware/database/connection.go similarity index 83% rename from pkg/database/connection.go rename to middleware/database/connection.go index ca46f81..9a47240 100644 --- a/pkg/database/connection.go +++ b/middleware/database/connection.go @@ -4,9 +4,9 @@ package database import ( - "github.com/sonr-io/motr/internal/sink/activity" - "github.com/sonr-io/motr/internal/sink/network" - "github.com/sonr-io/motr/internal/sink/users" + "github.com/sonr-io/motr/internal/db/activity" + "github.com/sonr-io/motr/internal/db/network" + "github.com/sonr-io/motr/internal/db/users" ) // connection is a database context diff --git a/pkg/database/middleware.go b/middleware/database/middleware.go similarity index 100% rename from pkg/database/middleware.go rename to middleware/database/middleware.go diff --git a/pkg/headers/headers.go b/middleware/headers/headers.go similarity index 100% rename from pkg/headers/headers.go rename to middleware/headers/headers.go diff --git a/middleware/marketapi/middleware.go b/middleware/marketapi/middleware.go new file mode 100644 index 0000000..37ba56e --- /dev/null +++ b/middleware/marketapi/middleware.go @@ -0,0 +1,77 @@ +//go:build js && wasm +// +build js,wasm + +package marketapi + +import ( + "errors" + "fmt" + + "github.com/labstack/echo/v4" + "github.com/syumai/workers/cloudflare/fetch" +) + +const ( + kCryptoAPIURL = "https://api.alternative.me" + kCryptoAPIListings = "/v2/listings" + kCryptoAPITickers = "/v2/ticker" + kCryptoAPIGlobal = "/v2/global" +) + +type Context struct { + echo.Context + client *fetch.Client +} + +func (c *Context) Listings(symbol string) (*ListingsResponse, error) { + r := c.buildRequest(fmt.Sprintf("%s/%s", kCryptoAPIListings, symbol)) + v := &ListingsResponse{} + err := c.fetch(r, v) + if err != nil { + return nil, err + } + return v, nil +} + +func (c *Context) Ticker(symbol string) (*TickersResponse, error) { + r := c.buildRequest(fmt.Sprintf("%s/%s", kCryptoAPITickers, symbol)) + v := &TickersResponse{} + err := c.fetch(r, v) + if err != nil { + return nil, err + } + return v, nil +} + +func (c *Context) GlobalMarket() (*GlobalMarketResponse, error) { + r := c.buildRequest(kCryptoAPIGlobal) + v := &GlobalMarketResponse{} + err := c.fetch(r, v) + if err != nil { + return nil, err + } + return v, nil +} + +// Middleware is a middleware that adds a new key to the context +func Middleware() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + f := fetch.NewClient() + return func(c echo.Context) error { + ctx := &Context{ + Context: c, + client: f, + } + 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/marketapi/types.go b/middleware/marketapi/types.go new file mode 100644 index 0000000..6d66d00 --- /dev/null +++ b/middleware/marketapi/types.go @@ -0,0 +1,98 @@ +//go:build js && wasm +// +build js,wasm + +package marketapi + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/syumai/workers/cloudflare/fetch" +) + +type Response interface { + UnmarshalJSON(data []byte) error +} + +type ListingsResponse struct { + Data []struct { + ID string `json:"id"` + Name string `json:"name"` + Symbol string `json:"symbol"` + WebsiteSlug string `json:"website_slug"` + } `json:"data"` + Metadata struct { + Timestamp int `json:"timestamp"` + NumCryptocurrencies int `json:"num_cryptocurrencies"` + Error any `json:"error"` + } `json:"metadata"` +} + +func (r *ListingsResponse) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, r) +} + +type TickersResponse struct { + Data []struct { + Symbol string `json:"symbol"` + Price struct { + USD float64 `json:"USD"` + } `json:"price"` + } `json:"data"` + Metadata struct { + Timestamp int `json:"timestamp"` + Error any `json:"error"` + } `json:"metadata"` +} + +func (r *TickersResponse) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, r) +} + +type GlobalMarketResponse struct { + Data []struct { + Symbol string `json:"symbol"` + Price struct { + USD float64 `json:"USD"` + } `json:"price"` + } `json:"data"` + Metadata struct { + Timestamp int `json:"timestamp"` + Error any `json:"error"` + } `json:"metadata"` +} + +func (r *GlobalMarketResponse) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, r) +} + +func (c *Context) buildRequest(url string) *fetch.Request { + r, err := fetch.NewRequest(c.Request().Context(), http.MethodGet, url, nil) + if err != nil { + fmt.Println(err) + return nil + } + r.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0") + return r +} + +func (c *Context) fetch(r *fetch.Request, v Response) error { + resp, err := c.client.Do(r, nil) + if err != nil { + return fmt.Errorf("request failed: %w", err) + } + defer resp.Body.Close() // Ensure body is always closed + + // Check for non-200 status codes + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + // Directly decode JSON into the response struct + if err := json.NewDecoder(resp.Body).Decode(v); err != nil { + return fmt.Errorf("failed to decode response: %w", err) + } + + return nil +} diff --git a/pkg/render/render.go b/middleware/render/render.go similarity index 100% rename from pkg/render/render.go rename to middleware/render/render.go diff --git a/pkg/session/metadata.templ b/middleware/session/metadata.templ similarity index 100% rename from pkg/session/metadata.templ rename to middleware/session/metadata.templ diff --git a/pkg/session/middleware.go b/middleware/session/middleware.go similarity index 96% rename from pkg/session/middleware.go rename to middleware/session/middleware.go index 4cdbcde..fecd9a7 100644 --- a/pkg/session/middleware.go +++ b/middleware/session/middleware.go @@ -6,7 +6,7 @@ package session import ( "github.com/labstack/echo/v4" "github.com/segmentio/ksuid" - "github.com/sonr-io/motr/pkg/cookies" + "github.com/sonr-io/motr/middleware/cookies" ) // Context is a session context diff --git a/middleware/sonrapi/middleware.go b/middleware/sonrapi/middleware.go new file mode 100644 index 0000000..5e9e653 --- /dev/null +++ b/middleware/sonrapi/middleware.go @@ -0,0 +1,42 @@ +//go:build js && wasm +// +build js,wasm + +package sonrapi + +import ( + "errors" + + "github.com/labstack/echo/v4" +) + +const ( + kCryptoAPIURL = "https://api.alternative.me" + kCryptoAPIListings = "/v2/listings" + kCryptoAPITickers = "/v2/ticker" + kCryptoAPIGlobal = "/v2/global" +) + +type Context struct { + echo.Context +} + +// Middleware is a middleware that adds a new key to the context +func Middleware() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ctx := &Context{ + Context: c, + } + 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/webauthn/assertion.go b/middleware/webauthn/assertion.go new file mode 100644 index 0000000..d26e260 --- /dev/null +++ b/middleware/webauthn/assertion.go @@ -0,0 +1 @@ +package webauthn diff --git a/middleware/webauthn/attestation.go b/middleware/webauthn/attestation.go new file mode 100644 index 0000000..d357b9e --- /dev/null +++ b/middleware/webauthn/attestation.go @@ -0,0 +1,9 @@ +package webauthn + +type LoginOptions struct { + Account string + Handle string + HelpText string + Label string + Challenge string +} diff --git a/middleware/webauthn/middleware.go b/middleware/webauthn/middleware.go new file mode 100644 index 0000000..8a031c4 --- /dev/null +++ b/middleware/webauthn/middleware.go @@ -0,0 +1,35 @@ +//go:build js && wasm +// +build js,wasm + +package webauthn + +import ( + "errors" + + "github.com/labstack/echo/v4" +) + +type Context struct { + echo.Context +} + +// Middleware is a middleware that adds a new key to the context +func Middleware() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ctx := &Context{ + Context: c, + } + 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/pkg/webauth/credential_descriptor.go b/middleware/webauthn/types.go similarity index 95% rename from pkg/webauth/credential_descriptor.go rename to middleware/webauthn/types.go index 339f5d3..2baa57e 100644 --- a/pkg/webauth/credential_descriptor.go +++ b/middleware/webauthn/types.go @@ -1,6 +1,6 @@ -package webauth +package webauthn -import models "github.com/sonr-io/motr/internal/sink/users" +import models "github.com/sonr-io/motr/internal/db/users" // Define the credential structure matching our frontend data type CredentialDescriptor struct {