Implement Database Migrations (#12)

* feat: enhance modularity by relocating core packages

* refactor: move chart components to dashboard package

* refactor: restructure database access layer

* refactor: rename credential descriptor to credentials for clarity

* feat: enhance development environment configuration for database interactions

* feat: integrate go-task for database migrations

* feat: introduce middleware for market data and WebAuthn
This commit is contained in:
2025-05-29 15:07:20 -04:00
committed by GitHub
parent 7fdfd5f570
commit 786fef8399
93 changed files with 2036 additions and 1046 deletions

View File

@@ -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

View File

@@ -33,3 +33,6 @@ release:
templ-watch:
@devbox run watch:templ
migrate:
@devbox run db:migrate

View File

@@ -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():
}
}

View File

@@ -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]]

View File

@@ -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():
}
}

View File

@@ -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]]

View File

@@ -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"

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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())

View File

@@ -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 {

View File

@@ -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"`

View File

@@ -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)
}

View File

@@ -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 = ?;

View File

@@ -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

View File

@@ -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);

View File

@@ -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"`

View File

@@ -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)
}

View File

@@ -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 (

View File

@@ -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

View File

@@ -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);

1
internal/jobs/cron.go Normal file
View File

@@ -0,0 +1 @@
package jobs

12
internal/jobs/events.go Normal file
View File

@@ -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
)

1
internal/jobs/tasks.go Normal file
View File

@@ -0,0 +1 @@
package jobs

View File

@@ -0,0 +1 @@
DROP TABLE accounts;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE credential;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE profiles;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE vaults;

View File

@@ -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)

View File

@@ -0,0 +1 @@
DROP TABLE assets;

View File

@@ -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);

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE price_conversions;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE blockchains;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE services;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE activities;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE global_market;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE fear_greed_index;

View File

@@ -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);

View File

@@ -0,0 +1 @@
DROP TABLE crypto_listings;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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()

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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"
)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -0,0 +1 @@
package webauthn

View File

@@ -0,0 +1,9 @@
package webauthn
type LoginOptions struct {
Account string
Handle string
HelpText string
Label string
Challenge string
}

View File

@@ -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
}

View File

@@ -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 {