feat: implement account, profile, network, and activity management

This commit is contained in:
2025-06-07 10:40:04 +08:00
commit 2f8cd28ee7
135 changed files with 11665 additions and 0 deletions

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
generate:
@gum spin --show-error --title "(1/3) Running templ generate" -- sh -c "templ generate"
@gum log --level info --time kitchen "[GENERATE] Completed templ generate successfully."
assets:
@gum spin --show-error --title "(2/3) Running workers-assets-gen" -- sh -c "go run github.com/syumai/workers/cmd/workers-assets-gen -mode=go"
@gum log --level info --time kitchen "[GENERATE] Completed workers-assets-gen successfully."
build: generate assets
@GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o ./build/app.wasm .
@gum log --level info --time kitchen "[BUILD] Completed Go WASM Build successfully."

69
config/config.go Normal file
View File

@@ -0,0 +1,69 @@
//go:build js && wasm
// +build js,wasm
package config
import (
"time"
"github.com/syumai/workers/cloudflare"
)
type Config struct {
Cache CacheSettings `json:"cache"` // Added Cache configuration
Sonr NetworkParams `json:"network"`
DefaultExpiry time.Duration `json:"expiry"`
}
type NetworkParams struct {
SonrChainID string `json:"sonr_chain_id"`
SonrAPIURL string `json:"sonr_api_url"`
SonrRPCURL string `json:"sonr_rpc_url"`
IPFSGateway string `json:"ipfs_gateway"`
}
// CacheSettings defines the configuration for Cloudflare cache
type CacheSettings struct {
Enabled bool `json:"enabled"`
DefaultMaxAge int `json:"default_max_age"`
BypassHeader string `json:"bypass_header"`
BypassValue string `json:"bypass_value"`
CacheableStatusCodes []int `json:"cacheable_status_codes"`
CacheableContentTypes []string `json:"cacheable_content_types"`
}
func Get() Config {
cache := CacheSettings{
Enabled: true,
DefaultMaxAge: 60, // 1 minute by default
BypassHeader: "X-Cache-Bypass",
BypassValue: "true",
CacheableStatusCodes: []int{
200, 301, 302,
},
CacheableContentTypes: []string{
"text/html",
"text/css",
"text/javascript",
"application/javascript",
"application/json",
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
},
}
sonr := NetworkParams{
SonrChainID: cloudflare.Getenv("SONR_CHAIN_ID"),
SonrAPIURL: cloudflare.Getenv("SONR_API_URL"),
SonrRPCURL: cloudflare.Getenv("SONR_RPC_URL"),
IPFSGateway: cloudflare.Getenv("IPFS_GATEWAY"),
}
c := Config{
Sonr: sonr,
Cache: cache,
DefaultExpiry: time.Hour * 1,
}
return c
}

28
config/routes.go Normal file
View File

@@ -0,0 +1,28 @@
//go:build js && wasm
// +build js,wasm
package config
import (
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/handlers"
)
// ╭────────────────────────────────────────────────╮
// │ HTTP Routes │
// ╰────────────────────────────────────────────────╯
func RegisterViews(e *echo.Echo) {
e.GET("/", handlers.RenderHomePage)
e.GET("/login", handlers.RenderLoginPage)
e.GET("/register", handlers.RenderRegisterPage)
}
func RegisterPartials(e *echo.Echo) {
e.POST("/login/:handle/check", handlers.HandleLoginCheck)
e.POST("/login/:handle/finish", handlers.HandleLoginFinish)
e.POST("/register/:handle", handlers.HandleRegisterStart)
e.POST("/register/:handle/check", handlers.HandleRegisterCheck)
e.POST("/register/:handle/finish", handlers.HandleRegisterFinish)
e.POST("/status", handlers.HandleStatusCheck)
}

40
handlers/auth_handler.go Normal file
View File

@@ -0,0 +1,40 @@
package handlers
import (
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/pkg/render"
"github.com/sonr-io/motr/ui/login"
"github.com/sonr-io/motr/ui/register"
)
func HandleLoginCheck(c echo.Context) error {
return render.Component(c, login.LoginView())
}
func HandleLoginInitial(c echo.Context) error {
return render.Component(c, login.LoginView())
}
func HandleLoginFinish(c echo.Context) error {
return render.Component(c, login.LoginView())
}
func HandleLoginStart(c echo.Context) error {
return render.Component(c, login.LoginView())
}
func HandleRegisterInitial(c echo.Context) error {
return render.Component(c, register.RegisterView())
}
func HandleRegisterCheck(c echo.Context) error {
return render.Component(c, register.RegisterView())
}
func HandleRegisterFinish(c echo.Context) error {
return render.Component(c, register.RegisterView())
}
func HandleRegisterStart(c echo.Context) error {
return render.Component(c, register.RegisterView())
}

4
handlers/form_handler.go Normal file
View File

@@ -0,0 +1,4 @@
//go:build js && wasm
// +build js,wasm
package handlers

24
handlers/page_handler.go Normal file
View File

@@ -0,0 +1,24 @@
//go:build js && wasm
// +build js,wasm
package handlers
import (
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/ui/register"
"github.com/sonr-io/motr/pkg/render"
"github.com/sonr-io/motr/ui/home"
"github.com/sonr-io/motr/ui/login"
)
func RenderHomePage(c echo.Context) error {
return render.Component(c, home.HomeView())
}
func RenderLoginPage(c echo.Context) error {
return render.Component(c, login.LoginView())
}
func RenderRegisterPage(c echo.Context) error {
return render.Component(c, register.RegisterView())
}

View File

@@ -0,0 +1,38 @@
package handlers
import (
"github.com/labstack/echo/v4"
)
// StatusCheck is a struct that represents the status of the application
type StatusCheck struct {
Ok bool `json:"ok"`
Services []struct {
Name string `json:"name"`
Ok bool `json:"ok"`
} `json:"services"`
}
// HandleStatusCheck is a handler that checks the status of the application
func HandleStatusCheck(c echo.Context) error {
return c.JSON(200, StatusCheck{
Ok: true,
Services: []struct {
Name string `json:"name"`
Ok bool `json:"ok"`
}{
{
Name: "IPFS",
Ok: true,
},
{
Name: "IBC",
Ok: true,
},
{
Name: "Sonr",
Ok: true,
},
},
})
}

35
internal/api/client.go Normal file
View File

@@ -0,0 +1,35 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"github.com/syumai/workers/cloudflare/fetch"
)
type Response interface {
UnmarshalJSON(data []byte) error
}
type Client interface {
MarketAPI
}
type client struct {
fc *fetch.Client
ctx context.Context
MarketAPI
}
func NewClient(ctx context.Context) *client {
fc := fetch.NewClient()
c := &client{
fc: fc,
ctx: ctx,
}
marketAPI := NewMarketAPI(c, ctx)
c.MarketAPI = marketAPI
return c
}

117
internal/api/market.go Normal file
View File

@@ -0,0 +1,117 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"encoding/json"
"fmt"
)
const (
kCryptoAPIURL = "https://api.alternative.me"
kCryptoAPIListings = "/v2/listings"
kCryptoAPITickers = "/v2/ticker"
kCryptoAPIGlobal = "/v2/global"
)
type MarketAPI interface {
Listings(symbol string) (*ListingsResponse, error)
Ticker(symbol string) (*TickersResponse, error)
GlobalMarket() (*GlobalMarketResponse, error)
}
type marketAPI struct {
client *client
ctx context.Context
}
func NewMarketAPI(c *client, ctx context.Context) *marketAPI {
return &marketAPI{
client: c,
ctx: ctx,
}
}
func (m *marketAPI) Listings(symbol string) (*ListingsResponse, error) {
r := buildRequest(m.ctx, fmt.Sprintf("%s/%s", kCryptoAPIListings, symbol))
v := &ListingsResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
func (m *marketAPI) Ticker(symbol string) (*TickersResponse, error) {
r := buildRequest(m.ctx, fmt.Sprintf("%s/%s", kCryptoAPITickers, symbol))
v := &TickersResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
func (m *marketAPI) GlobalMarket() (*GlobalMarketResponse, error) {
r := buildRequest(m.ctx, kCryptoAPIGlobal)
v := &GlobalMarketResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
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)
}

43
internal/api/utils.go Normal file
View File

@@ -0,0 +1,43 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/syumai/workers/cloudflare/fetch"
)
func buildRequest(c context.Context, url string) *fetch.Request {
r, err := fetch.NewRequest(c, 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 doFetch(c *fetch.Client, r *fetch.Request, v Response) error {
resp, err := c.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

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,99 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"database/sql"
"time"
)
type Activity struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
AccountID string `json:"account_id"`
TxHash sql.NullString `json:"tx_hash"`
TxType string `json:"tx_type"`
Status string `json:"status"`
Amount sql.NullString `json:"amount"`
Fee sql.NullString `json:"fee"`
GasUsed sql.NullInt64 `json:"gas_used"`
GasWanted sql.NullInt64 `json:"gas_wanted"`
Memo sql.NullString `json:"memo"`
BlockHeight sql.NullInt64 `json:"block_height"`
Timestamp time.Time `json:"timestamp"`
RawLog sql.NullString `json:"raw_log"`
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"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
EndpointUrl string `json:"endpoint_url"`
EndpointType string `json:"endpoint_type"`
ChainID sql.NullString `json:"chain_id"`
Status string `json:"status"`
ResponseTimeMs sql.NullInt64 `json:"response_time_ms"`
LastChecked time.Time `json:"last_checked"`
NextCheck sql.NullTime `json:"next_check"`
FailureCount int64 `json:"failure_count"`
SuccessCount int64 `json:"success_count"`
ResponseData sql.NullString `json:"response_data"`
ErrorMessage sql.NullString `json:"error_message"`
}
type Service struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
ChainID string `json:"chain_id"`
Address string `json:"address"`
OwnerAddress string `json:"owner_address"`
Metadata sql.NullString `json:"metadata"`
Status string `json:"status"`
BlockHeight int64 `json:"block_height"`
}

View File

@@ -0,0 +1,63 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"context"
"database/sql"
)
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)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,338 @@
-- name: InsertService :one
INSERT INTO services (
name,
description,
chain_id,
address,
owner_address,
metadata,
status,
block_height
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetServiceByID :one
SELECT * FROM services
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetServiceByAddress :one
SELECT * FROM services
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetServiceByChainAndAddress :one
SELECT * FROM services
WHERE chain_id = ? AND address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListServicesByChain :many
SELECT * FROM services
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY name ASC
LIMIT ? OFFSET ?;
-- name: ListServicesByOwner :many
SELECT * FROM services
WHERE owner_address = ? AND deleted_at IS NULL
ORDER BY created_at DESC
LIMIT ? OFFSET ?;
-- name: UpdateService :one
UPDATE services
SET
name = ?,
description = ?,
owner_address = ?,
metadata = ?,
status = ?,
block_height = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteService :exec
UPDATE services
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- ACTIVITY QUERIES
-- name: InsertActivity :one
INSERT INTO activities (
account_id,
tx_hash,
tx_type,
status,
amount,
fee,
gas_used,
gas_wanted,
memo,
block_height,
timestamp,
raw_log,
error
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetActivityByID :one
SELECT * FROM activities
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetActivityByTxHash :one
SELECT * FROM activities
WHERE tx_hash = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListActivitiesByAccount :many
SELECT * FROM activities
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: ListActivitiesByType :many
SELECT * FROM activities
WHERE tx_type = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: ListActivitiesByStatus :many
SELECT * FROM activities
WHERE status = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: UpdateActivityStatus :one
UPDATE activities
SET
status = ?,
tx_hash = ?,
block_height = ?,
gas_used = ?,
raw_log = ?,
error = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteActivity :exec
UPDATE activities
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- HEALTH QUERIES
-- name: InsertHealth :one
INSERT INTO health (
endpoint_url,
endpoint_type,
chain_id,
status,
response_time_ms,
last_checked,
next_check,
failure_count,
success_count,
response_data,
error_message
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetHealthByID :one
SELECT * FROM health
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetHealthByEndpoint :one
SELECT * FROM health
WHERE endpoint_url = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT 1;
-- name: ListHealthByChain :many
SELECT * FROM health
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT ? OFFSET ?;
-- name: ListHealthByStatus :many
SELECT * FROM health
WHERE status = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT ? OFFSET ?;
-- name: ListHealthChecksNeedingUpdate :many
SELECT * FROM health
WHERE next_check <= CURRENT_TIMESTAMP AND deleted_at IS NULL
ORDER BY next_check ASC
LIMIT ?;
-- name: UpdateHealthCheck :one
UPDATE health
SET
status = ?,
response_time_ms = ?,
last_checked = CURRENT_TIMESTAMP,
next_check = ?,
failure_count = CASE WHEN status = 'failed' THEN failure_count + 1 ELSE failure_count END,
success_count = CASE WHEN status = 'success' THEN success_count + 1 ELSE success_count END,
response_data = ?,
error_message = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteHealth :exec
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 = ?;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
-- 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)
);
-- 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)
);
-- 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)
);
-- 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);
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);
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);
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);
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);

31
internal/db/network/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,96 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"database/sql"
"time"
)
type Asset struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int64 `json:"decimals"`
ChainID string `json:"chain_id"`
Channel string `json:"channel"`
AssetType string `json:"asset_type"`
CoingeckoID sql.NullString `json:"coingecko_id"`
}
type Blockchain struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
ChainName string `json:"chain_name"`
ChainIDCosmos sql.NullString `json:"chain_id_cosmos"`
ChainIDEvm sql.NullString `json:"chain_id_evm"`
ApiName sql.NullString `json:"api_name"`
BechAccountPrefix sql.NullString `json:"bech_account_prefix"`
BechValidatorPrefix sql.NullString `json:"bech_validator_prefix"`
MainAssetSymbol sql.NullString `json:"main_asset_symbol"`
MainAssetDenom sql.NullString `json:"main_asset_denom"`
StakingAssetSymbol sql.NullString `json:"staking_asset_symbol"`
StakingAssetDenom sql.NullString `json:"staking_asset_denom"`
IsStakeEnabled bool `json:"is_stake_enabled"`
ChainImage sql.NullString `json:"chain_image"`
MainAssetImage sql.NullString `json:"main_asset_image"`
StakingAssetImage sql.NullString `json:"staking_asset_image"`
ChainType string `json:"chain_type"`
IsSupportMobileWallet bool `json:"is_support_mobile_wallet"`
IsSupportExtensionWallet bool `json:"is_support_extension_wallet"`
IsSupportErc20 bool `json:"is_support_erc20"`
DescriptionEn sql.NullString `json:"description_en"`
DescriptionKo sql.NullString `json:"description_ko"`
DescriptionJa sql.NullString `json:"description_ja"`
OriginGenesisTime sql.NullTime `json:"origin_genesis_time"`
AccountType string `json:"account_type"`
BtcStaking sql.NullString `json:"btc_staking"`
CosmosFeeInfo sql.NullString `json:"cosmos_fee_info"`
EvmFeeInfo sql.NullString `json:"evm_fee_info"`
LcdEndpoint sql.NullString `json:"lcd_endpoint"`
GrpcEndpoint sql.NullString `json:"grpc_endpoint"`
EvmRpcEndpoint sql.NullString `json:"evm_rpc_endpoint"`
Explorer sql.NullString `json:"explorer"`
About sql.NullString `json:"about"`
Forum sql.NullString `json:"forum"`
}
type Price struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
AssetID string `json:"asset_id"`
PriceUsd sql.NullFloat64 `json:"price_usd"`
PriceBtc sql.NullFloat64 `json:"price_btc"`
Volume24hUsd sql.NullFloat64 `json:"volume_24h_usd"`
MarketCapUsd sql.NullFloat64 `json:"market_cap_usd"`
AvailableSupply sql.NullFloat64 `json:"available_supply"`
TotalSupply sql.NullFloat64 `json:"total_supply"`
MaxSupply sql.NullFloat64 `json:"max_supply"`
PercentChange1h sql.NullFloat64 `json:"percent_change_1h"`
PercentChange24h sql.NullFloat64 `json:"percent_change_24h"`
PercentChange7d sql.NullFloat64 `json:"percent_change_7d"`
Rank sql.NullInt64 `json:"rank"`
LastUpdated time.Time `json:"last_updated"`
}
type PriceConversion struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
PriceID string `json:"price_id"`
CurrencyCode string `json:"currency_code"`
Price sql.NullFloat64 `json:"price"`
Volume24h sql.NullFloat64 `json:"volume_24h"`
MarketCap sql.NullFloat64 `json:"market_cap"`
LastUpdated time.Time `json:"last_updated"`
}

View File

@@ -0,0 +1,64 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"context"
"database/sql"
)
type Querier interface {
CountBlockchainsByChainType(ctx context.Context, dollar_1 sql.NullString) (int64, error)
GetAssetByChainAndSymbol(ctx context.Context, arg GetAssetByChainAndSymbolParams) (Asset, error)
GetAssetByID(ctx context.Context, id string) (Asset, error)
GetAssetBySymbol(ctx context.Context, symbol string) (Asset, error)
GetAssetWithLatestPrice(ctx context.Context, id string) (GetAssetWithLatestPriceRow, error)
GetBlockchainByChainName(ctx context.Context, chainName string) (Blockchain, error)
GetBlockchainByCosmosChainID(ctx context.Context, chainIDCosmos sql.NullString) (Blockchain, error)
GetBlockchainByEvmChainID(ctx context.Context, chainIDEvm sql.NullString) (Blockchain, error)
GetBlockchainByID(ctx context.Context, id string) (Blockchain, error)
GetBlockchainEndpoints(ctx context.Context, id string) (GetBlockchainEndpointsRow, error)
GetBlockchainExplorer(ctx context.Context, id string) (GetBlockchainExplorerRow, error)
GetBlockchainWithAssetInfo(ctx context.Context, id string) (GetBlockchainWithAssetInfoRow, 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)
GetPriceConversionByID(ctx context.Context, id string) (PriceConversion, error)
GetPriceConversionsByPriceID(ctx context.Context, priceID string) ([]PriceConversion, error)
// ASSET QUERIES
InsertAsset(ctx context.Context, arg InsertAssetParams) (Asset, error)
// BLOCKCHAIN QUERIES
InsertBlockchain(ctx context.Context, arg InsertBlockchainParams) (Blockchain, error)
// PRICE QUERIES (UPDATED)
InsertPrice(ctx context.Context, arg InsertPriceParams) (Price, error)
// PRICE CONVERSION QUERIES (NEW)
InsertPriceConversion(ctx context.Context, arg InsertPriceConversionParams) (PriceConversion, error)
ListAllBlockchains(ctx context.Context) ([]Blockchain, error)
ListAssetsByChain(ctx context.Context, chainID string) ([]Asset, error)
ListAssetsWithLatestPrices(ctx context.Context, arg ListAssetsWithLatestPricesParams) ([]ListAssetsWithLatestPricesRow, error)
ListBlockchainsByChainType(ctx context.Context, dollar_1 sql.NullString) ([]Blockchain, error)
ListBlockchainsWithAssetInfo(ctx context.Context, arg ListBlockchainsWithAssetInfoParams) ([]ListBlockchainsWithAssetInfoRow, error)
ListBlockchainsWithERC20Support(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithExtensionSupport(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithMobileSupport(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithStaking(ctx context.Context) ([]Blockchain, 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
SoftDeletePriceConversion(ctx context.Context, id string) error
UpdateAsset(ctx context.Context, arg UpdateAssetParams) (Asset, error)
UpdateBlockchain(ctx context.Context, arg UpdateBlockchainParams) (Blockchain, error)
UpdateBlockchainDescriptions(ctx context.Context, arg UpdateBlockchainDescriptionsParams) (Blockchain, error)
UpdateBlockchainEndpoints(ctx context.Context, arg UpdateBlockchainEndpointsParams) (Blockchain, error)
UpdateBlockchainExplorer(ctx context.Context, arg UpdateBlockchainExplorerParams) (Blockchain, error)
UpdateBlockchainFeeInfo(ctx context.Context, arg UpdateBlockchainFeeInfoParams) (Blockchain, error)
UpdateBlockchainImages(ctx context.Context, arg UpdateBlockchainImagesParams) (Blockchain, error)
UpdateBlockchainSocialLinks(ctx context.Context, arg UpdateBlockchainSocialLinksParams) (Blockchain, error)
UpdatePrice(ctx context.Context, arg UpdatePriceParams) (Price, error)
UpdatePriceConversion(ctx context.Context, arg UpdatePriceConversionParams) (PriceConversion, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,453 @@
-- ASSET QUERIES
-- name: InsertAsset :one
INSERT INTO assets (
name,
symbol,
decimals,
chain_id,
channel,
asset_type,
coingecko_id
) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetAssetByID :one
SELECT * FROM assets
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAssetBySymbol :one
SELECT * FROM assets
WHERE symbol = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAssetByChainAndSymbol :one
SELECT * FROM assets
WHERE chain_id = ? AND symbol = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListAssetsByChain :many
SELECT * FROM assets
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY symbol ASC;
-- name: UpdateAsset :one
UPDATE assets
SET
name = ?,
decimals = ?,
channel = ?,
asset_type = ?,
coingecko_id = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteAsset :exec
UPDATE assets
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- PRICE QUERIES (UPDATED)
-- name: InsertPrice :one
INSERT INTO prices (
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
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetPriceByAssetID :one
SELECT * FROM prices
WHERE asset_id = ? AND deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT 1;
-- name: GetPriceByID :one
SELECT * FROM prices
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListPriceHistoryByAssetID :many
SELECT * FROM prices
WHERE asset_id = ? AND deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT ? OFFSET ?;
-- name: GetAssetWithLatestPrice :one
SELECT a.*, p.price_usd, p.price_btc, p.volume_24h_usd, p.market_cap_usd,
p.available_supply, p.total_supply, p.max_supply,
p.percent_change_1h, p.percent_change_24h, p.percent_change_7d,
p.rank, p.last_updated
FROM assets a
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE a.id = ? AND a.deleted_at IS NULL
LIMIT 1;
-- name: ListAssetsWithLatestPrices :many
SELECT a.*, p.price_usd, p.price_btc, p.volume_24h_usd, p.market_cap_usd,
p.available_supply, p.total_supply, p.max_supply,
p.percent_change_1h, p.percent_change_24h, p.percent_change_7d,
p.rank, p.last_updated
FROM assets a
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE a.deleted_at IS NULL
ORDER BY p.rank ASC, a.symbol ASC
LIMIT ? OFFSET ?;
-- name: UpdatePrice :one
UPDATE prices
SET
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 = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- PRICE CONVERSION QUERIES (NEW)
-- name: InsertPriceConversion :one
INSERT INTO price_conversions (
price_id,
currency_code,
price,
volume_24h,
market_cap,
last_updated
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetPriceConversionByID :one
SELECT * FROM price_conversions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetPriceConversionsByPriceID :many
SELECT * FROM price_conversions
WHERE price_id = ? AND deleted_at IS NULL
ORDER BY currency_code ASC;
-- name: GetPriceConversionByCurrency :one
SELECT * FROM price_conversions
WHERE price_id = ? AND currency_code = ? AND deleted_at IS NULL
LIMIT 1;
-- name: UpdatePriceConversion :one
UPDATE price_conversions
SET
price = ?,
volume_24h = ?,
market_cap = ?,
last_updated = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeletePriceConversion :exec
UPDATE price_conversions
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- BLOCKCHAIN QUERIES
-- name: InsertBlockchain :one
INSERT INTO blockchains (
id,
chain_name,
chain_id_cosmos,
chain_id_evm,
api_name,
bech_account_prefix,
bech_validator_prefix,
main_asset_symbol,
main_asset_denom,
staking_asset_symbol,
staking_asset_denom,
is_stake_enabled,
chain_image,
main_asset_image,
staking_asset_image,
chain_type,
is_support_mobile_wallet,
is_support_extension_wallet,
is_support_erc20,
description_en,
description_ko,
description_ja,
origin_genesis_time,
account_type,
btc_staking,
cosmos_fee_info,
evm_fee_info,
lcd_endpoint,
grpc_endpoint,
evm_rpc_endpoint,
explorer,
about,
forum
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: GetBlockchainByID :one
SELECT * FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByChainName :one
SELECT * FROM blockchains
WHERE chain_name = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByCosmosChainID :one
SELECT * FROM blockchains
WHERE chain_id_cosmos = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByEvmChainID :one
SELECT * FROM blockchains
WHERE chain_id_evm = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListAllBlockchains :many
SELECT * FROM blockchains
WHERE deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsByChainType :many
SELECT * FROM blockchains
WHERE chain_type LIKE '%' || ? || '%' AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithStaking :many
SELECT * FROM blockchains
WHERE is_stake_enabled = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithMobileSupport :many
SELECT * FROM blockchains
WHERE is_support_mobile_wallet = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithExtensionSupport :many
SELECT * FROM blockchains
WHERE is_support_extension_wallet = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithERC20Support :many
SELECT * FROM blockchains
WHERE is_support_erc20 = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: UpdateBlockchain :one
UPDATE blockchains
SET
chain_name = ?,
chain_id_cosmos = ?,
chain_id_evm = ?,
api_name = ?,
bech_account_prefix = ?,
bech_validator_prefix = ?,
main_asset_symbol = ?,
main_asset_denom = ?,
staking_asset_symbol = ?,
staking_asset_denom = ?,
is_stake_enabled = ?,
chain_image = ?,
main_asset_image = ?,
staking_asset_image = ?,
chain_type = ?,
is_support_mobile_wallet = ?,
is_support_extension_wallet = ?,
is_support_erc20 = ?,
description_en = ?,
description_ko = ?,
description_ja = ?,
origin_genesis_time = ?,
account_type = ?,
btc_staking = ?,
cosmos_fee_info = ?,
evm_fee_info = ?,
lcd_endpoint = ?,
grpc_endpoint = ?,
evm_rpc_endpoint = ?,
explorer = ?,
about = ?,
forum = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainEndpoints :one
UPDATE blockchains
SET
lcd_endpoint = ?,
grpc_endpoint = ?,
evm_rpc_endpoint = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainExplorer :one
UPDATE blockchains
SET
explorer = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainFeeInfo :one
UPDATE blockchains
SET
cosmos_fee_info = ?,
evm_fee_info = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainImages :one
UPDATE blockchains
SET
chain_image = ?,
main_asset_image = ?,
staking_asset_image = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainDescriptions :one
UPDATE blockchains
SET
description_en = ?,
description_ko = ?,
description_ja = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainSocialLinks :one
UPDATE blockchains
SET
about = ?,
forum = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBlockchain :exec
UPDATE blockchains
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: GetBlockchainWithAssetInfo :one
SELECT b.*, a.id as asset_id, a.symbol, a.decimals, p.price_usd, p.price_btc
FROM blockchains b
LEFT JOIN assets a ON b.main_asset_symbol = a.symbol
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE b.id = ? AND b.deleted_at IS NULL
LIMIT 1;
-- name: ListBlockchainsWithAssetInfo :many
SELECT b.*, a.id as asset_id, a.symbol, a.decimals, p.price_usd, p.price_btc
FROM blockchains b
LEFT JOIN assets a ON b.main_asset_symbol = a.symbol
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE b.deleted_at IS NULL
ORDER BY b.chain_name ASC
LIMIT ? OFFSET ?;
-- name: SearchBlockchains :many
SELECT * FROM blockchains
WHERE (
chain_name LIKE '%' || ? || '%' OR
main_asset_symbol LIKE '%' || ? || '%' OR
staking_asset_symbol LIKE '%' || ? || '%' OR
description_en LIKE '%' || ? || '%'
) AND deleted_at IS NULL
ORDER BY chain_name ASC
LIMIT ? OFFSET ?;
-- name: CountBlockchainsByChainType :one
SELECT COUNT(*) as count FROM blockchains
WHERE chain_type LIKE '%' || ? || '%' AND deleted_at IS NULL;
-- name: GetBlockchainEndpoints :one
SELECT id, chain_name, lcd_endpoint, grpc_endpoint, evm_rpc_endpoint
FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainExplorer :one
SELECT id, chain_name, explorer
FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
-- 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)
);
-- 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)
);
-- 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)
);
-- 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))
);
-- Add all necessary indexes
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);
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);
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_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);

34
internal/db/sqlc.yaml Normal file
View File

@@ -0,0 +1,34 @@
version: "2"
sql:
# Activity DB - User to User Interactions
- engine: "sqlite"
queries: "./activity/query.sql"
schema: "./activity/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "activity"
out: "./activity"
# Network DB - Blockchain Parameters and Asset Metadata
- engine: "sqlite"
queries: "./network/query.sql"
schema: "./network/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "network"
out: "./network"
# Users DB - Accounts, Profiles, and Vault Metadata
- engine: "sqlite"
queries: "./users/query.sql"
schema: "./users/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "users"
out: "./users"

31
internal/db/users/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,68 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"database/sql"
"time"
)
type Account struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Number int64 `json:"number"`
Sequence int64 `json:"sequence"`
Address string `json:"address"`
PublicKey string `json:"public_key"`
ChainID string `json:"chain_id"`
BlockCreated int64 `json:"block_created"`
Controller string `json:"controller"`
Label string `json:"label"`
Handle string `json:"handle"`
IsSubsidiary bool `json:"is_subsidiary"`
IsValidator bool `json:"is_validator"`
IsDelegator bool `json:"is_delegator"`
IsAccountable bool `json:"is_accountable"`
}
type Credential struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
CredentialID string `json:"credential_id"`
AuthenticatorAttachment string `json:"authenticator_attachment"`
Origin string `json:"origin"`
Type string `json:"type"`
Transports string `json:"transports"`
}
type Profile struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Address string `json:"address"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Name string `json:"name"`
}
type Vault struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Address string `json:"address"`
Cid string `json:"cid"`
Config string `json:"config"`
SessionID string `json:"session_id"`
RedirectUri string `json:"redirect_uri"`
}

View File

@@ -0,0 +1,53 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"context"
)
type Querier interface {
CheckHandleExists(ctx context.Context, handle string) (bool, error)
GetAccountByAddress(ctx context.Context, address string) (Account, error)
GetAccountByController(ctx context.Context, controller string) (Account, error)
GetAccountByID(ctx context.Context, id string) (Account, error)
GetAccountByNumber(ctx context.Context, number int64) (Account, error)
GetAccountByPublicKey(ctx context.Context, publicKey string) (Account, error)
GetAccountBySequence(ctx context.Context, sequence int64) (Account, error)
GetAccountsByChainID(ctx context.Context, chainID string) ([]Account, error)
GetAccountsByController(ctx context.Context, controller string) ([]Account, error)
GetAccountsByHandle(ctx context.Context, handle string) ([]Account, error)
GetAccountsByLabel(ctx context.Context, label string) ([]Account, error)
GetCredentialByID(ctx context.Context, credentialID string) (Credential, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error)
GetProfileByAddress(ctx context.Context, address string) (Profile, error)
GetProfileByHandle(ctx context.Context, handle string) (Profile, error)
GetProfileByID(ctx context.Context, id string) (Profile, error)
GetVaultByID(ctx context.Context, id string) (Vault, error)
GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error)
GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error)
GetVaultsByHandle(ctx context.Context, handle string) ([]Vault, error)
// ACCOUNT QUERIES
InsertAccount(ctx context.Context, arg InsertAccountParams) (Account, error)
// CREDENTIAL QUERIES
InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error)
// PROFILE QUERIES
InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error)
// VAULT QUERIES
InsertVault(ctx context.Context, arg InsertVaultParams) (Vault, error)
ListDelegatorAccounts(ctx context.Context) ([]Account, error)
ListProfiles(ctx context.Context, arg ListProfilesParams) ([]Profile, error)
ListValidatorAccounts(ctx context.Context) ([]Account, error)
SoftDeleteAccount(ctx context.Context, id string) error
SoftDeleteCredential(ctx context.Context, credentialID string) error
SoftDeleteProfile(ctx context.Context, address string) error
SoftDeleteVault(ctx context.Context, id string) error
UpdateAccountLabel(ctx context.Context, arg UpdateAccountLabelParams) (Account, error)
UpdateAccountSequence(ctx context.Context, arg UpdateAccountSequenceParams) (Account, error)
UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error)
UpdateVault(ctx context.Context, arg UpdateVaultParams) (Vault, error)
}
var _ Querier = (*Queries)(nil)

234
internal/db/users/query.sql Normal file
View File

@@ -0,0 +1,234 @@
-- PROFILE QUERIES
-- name: InsertProfile :one
INSERT INTO profiles (
address,
handle,
origin,
name
) VALUES (?, ?, ?, ?)
RETURNING *;
-- name: GetProfileByID :one
SELECT * FROM profiles
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByAddress :one
SELECT * FROM profiles
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByHandle :one
SELECT * FROM profiles
WHERE handle = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = ?
AND deleted_at IS NULL;
-- name: UpdateProfile :one
UPDATE profiles
SET
name = ?,
handle = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = ?;
-- name: ListProfiles :many
SELECT * FROM profiles
WHERE deleted_at IS NULL
ORDER BY created_at DESC
LIMIT ? OFFSET ?;
-- VAULT QUERIES
-- name: InsertVault :one
INSERT INTO vaults (
handle,
origin,
address,
cid,
config,
session_id,
redirect_uri
) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetVaultByID :one
SELECT * FROM vaults
WHERE id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultsByHandle :many
SELECT * FROM vaults
WHERE handle = ?
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetVaultConfigByCID :one
SELECT * FROM vaults
WHERE cid = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: UpdateVault :one
UPDATE vaults
SET
config = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteVault :exec
UPDATE vaults
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- ACCOUNT QUERIES
-- name: InsertAccount :one
INSERT INTO accounts (
number,
sequence,
address,
public_key,
chain_id,
block_created,
controller,
label,
is_subsidiary,
is_validator,
is_delegator,
is_accountable
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetAccountByID :one
SELECT * FROM accounts
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByAddress :one
SELECT * FROM accounts
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountsByHandle :many
SELECT * FROM accounts
WHERE handle = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetAccountByController :one
SELECT * FROM accounts
WHERE controller = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByPublicKey :one
SELECT * FROM accounts
WHERE public_key = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByNumber :one
SELECT * FROM accounts
WHERE number = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountBySequence :one
SELECT * FROM accounts
WHERE sequence = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountsByChainID :many
SELECT * FROM accounts
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY sequence DESC;
-- name: GetAccountsByController :many
SELECT * FROM accounts
WHERE controller = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetAccountsByLabel :many
SELECT * FROM accounts
WHERE label = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: UpdateAccountSequence :one
UPDATE accounts
SET
sequence = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateAccountLabel :one
UPDATE accounts
SET
label = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteAccount :exec
UPDATE accounts
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: ListValidatorAccounts :many
SELECT * FROM accounts
WHERE is_validator = 1
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: ListDelegatorAccounts :many
SELECT * FROM accounts
WHERE is_delegator = 1
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- CREDENTIAL QUERIES
-- name: InsertCredential :one
INSERT INTO credentials (
handle,
credential_id,
authenticator_attachment,
origin,
type,
transports
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetCredentialsByHandle :many
SELECT * FROM credentials
WHERE handle = ?
AND deleted_at IS NULL;
-- name: GetCredentialByID :one
SELECT * FROM credentials
WHERE credential_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
WHERE credential_id = ?;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
-- 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
);
-- 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))
);
-- 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)
);
-- 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_credentials_handle ON credentials(handle);
CREATE INDEX idx_credentials_origin ON credentials(origin);
CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at);
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);
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);
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);

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 */1 * * *") // 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,97 @@
package meta
import "github.com/labstack/echo/v4"
func GetMetadata(c echo.Context) Metadata {
return DefaultMetadata()
}
func GetMetaComponent(c echo.Context) templ.Component {
return MetaComponent(GetMetadata(c))
}
func DefaultMetadata() Metadata {
return Metadata{
Title: "Motr",
Author: "Sonr",
Favicon: "https://cdn.sonr.id/favicon.png",
Robots: "index, follow",
Googlebot: "index, follow",
Google: "nositelinkssearchbox",
Description: "Sonr is a decentralized social network that allows you to create your own personalized digital identity.",
Keywords: "Sonr, social network, decentralized, identity, decentralized social network, decentralized identity, self-sovereign identity, self-sovereign, self-sovereign social network, self-sovereign identity network, sso, sso network, sso identity, sso social network, digital identity, digital social network",
CanonicalURL: "https://sonr.io",
OGImage: "https://cdn.sonr.id/og.png",
OGURL: "https://sonr.io",
OGSiteName: "Sonr",
TwitterSite: "@sonr_io",
TwitterCreator: "@sonr_io",
TwitterImage: "https://cdn.sonr.id/og.png",
}
}
type Metadata struct {
Title string
Author string
Favicon string
Robots string
Googlebot string
Google string
Description string
Keywords string
CanonicalURL string
OGImage string
OGURL string
OGSiteName string
TwitterSite string
TwitterCreator string
TwitterImage string
}
templ DefaultMetaComponent() {
<title>Motr</title>
<link rel="icon" type="image/png" href="https://cdn.sonr.id/favicon.png"/>
<meta name="description" content="Sonr is a decentralized social network that allows you to create your own personalized digital identity."/>
<meta name="keywords" content="Sonr, social network, decentralized, identity, decentralized social network, decentralized identity, self-sovereign identity, self-sovereign, self-sovereign social network, self-sovereign identity network, sso, sso network, sso identity, sso social network, digital identity, digital social network"/>
<meta name="author" content="Sonr"/>
<meta name="robots" content="index, follow"/>
<meta name="googlebot" content="index, follow"/>
<meta name="google" content="nositelinkssearchbox"/>
<meta property="og:title" content="Motr"/>
<meta property="og:description" content="Sonr is a decentralized social network that allows you to create your own personalized digital identity."/>
<meta property="og:image" content="https://cdn.sonr.id/og.png"/>
<meta property="og:url" content="https://sonr.io"/>
<meta property="og:site_name" content="Sonr"/>
<meta property="og:type" content="website"/>
<meta property="og:locale" content="en_US"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:site" content="@sonr_io"/>
<meta name="twitter:creator" content="@sonr_io"/>
<meta name="twitter:title" content="Motr"/>
<meta name="twitter:description" content="Sonr is a decentralized social network that allows you to create your own personalized digital identity."/>
<meta name="twitter:image" content="https://cdn.sonr.id/og.png"/>
}
templ MetaComponent(m Metadata) {
<title>{ m.Title }</title>
<link rel="icon" type="image/png" href={ m.Favicon }/>
<meta name="description" content={ m.Description }/>
<meta name="keywords" content={ m.Keywords }/>
<meta name="author" content={ m.Author }/>
<meta name="robots" content={ m.Robots }/>
<meta name="googlebot" content={ m.Googlebot }/>
<meta name="google" content={ m.Google }/>
<meta property="og:title" content={ m.Title }/>
<meta property="og:description" content={ m.Description }/>
<meta property="og:image" content={ m.OGImage }/>
<meta property="og:url" content={ m.OGURL }/>
<meta property="og:site_name" content={ m.OGSiteName }/>
<meta property="og:type" content="website"/>
<meta property="og:locale" content="en_US"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:site" content={ m.TwitterSite }/>
<meta name="twitter:creator" content={ m.TwitterCreator }/>
<meta name="twitter:title" content={ m.Title }/>
<meta name="twitter:description" content={ m.Description }/>
<meta name="twitter:image" content={ m.TwitterImage }/>
}

View File

@@ -0,0 +1,351 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package meta
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "github.com/labstack/echo/v4"
func GetMetadata(c echo.Context) Metadata {
return DefaultMetadata()
}
func GetMetaComponent(c echo.Context) templ.Component {
return MetaComponent(GetMetadata(c))
}
func DefaultMetadata() Metadata {
return Metadata{
Title: "Motr",
Author: "Sonr",
Favicon: "https://cdn.sonr.id/favicon.png",
Robots: "index, follow",
Googlebot: "index, follow",
Google: "nositelinkssearchbox",
Description: "Sonr is a decentralized social network that allows you to create your own personalized digital identity.",
Keywords: "Sonr, social network, decentralized, identity, decentralized social network, decentralized identity, self-sovereign identity, self-sovereign, self-sovereign social network, self-sovereign identity network, sso, sso network, sso identity, sso social network, digital identity, digital social network",
CanonicalURL: "https://sonr.io",
OGImage: "https://cdn.sonr.id/og.png",
OGURL: "https://sonr.io",
OGSiteName: "Sonr",
TwitterSite: "@sonr_io",
TwitterCreator: "@sonr_io",
TwitterImage: "https://cdn.sonr.id/og.png",
}
}
type Metadata struct {
Title string
Author string
Favicon string
Robots string
Googlebot string
Google string
Description string
Keywords string
CanonicalURL string
OGImage string
OGURL string
OGSiteName string
TwitterSite string
TwitterCreator string
TwitterImage string
}
func DefaultMetaComponent() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<title>Motr</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn.sonr.id/favicon.png\"><meta name=\"description\" content=\"Sonr is a decentralized social network that allows you to create your own personalized digital identity.\"><meta name=\"keywords\" content=\"Sonr, social network, decentralized, identity, decentralized social network, decentralized identity, self-sovereign identity, self-sovereign, self-sovereign social network, self-sovereign identity network, sso, sso network, sso identity, sso social network, digital identity, digital social network\"><meta name=\"author\" content=\"Sonr\"><meta name=\"robots\" content=\"index, follow\"><meta name=\"googlebot\" content=\"index, follow\"><meta name=\"google\" content=\"nositelinkssearchbox\"><meta property=\"og:title\" content=\"Motr\"><meta property=\"og:description\" content=\"Sonr is a decentralized social network that allows you to create your own personalized digital identity.\"><meta property=\"og:image\" content=\"https://cdn.sonr.id/og.png\"><meta property=\"og:url\" content=\"https://sonr.io\"><meta property=\"og:site_name\" content=\"Sonr\"><meta property=\"og:type\" content=\"website\"><meta property=\"og:locale\" content=\"en_US\"><meta name=\"twitter:card\" content=\"summary_large_image\"><meta name=\"twitter:site\" content=\"@sonr_io\"><meta name=\"twitter:creator\" content=\"@sonr_io\"><meta name=\"twitter:title\" content=\"Motr\"><meta name=\"twitter:description\" content=\"Sonr is a decentralized social network that allows you to create your own personalized digital identity.\"><meta name=\"twitter:image\" content=\"https://cdn.sonr.id/og.png\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func MetaComponent(m Metadata) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 76, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</title><link rel=\"icon\" type=\"image/png\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(m.Favicon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 77, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><meta name=\"description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(m.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 78, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"><meta name=\"keywords\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(m.Keywords)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 79, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><meta name=\"author\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 80, Col: 39}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><meta name=\"robots\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(m.Robots)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 81, Col: 39}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"><meta name=\"googlebot\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(m.Googlebot)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 82, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"><meta name=\"google\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(m.Google)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 83, Col: 39}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"><meta property=\"og:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 84, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"><meta property=\"og:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(m.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 85, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\"><meta property=\"og:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(m.OGImage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 86, Col: 46}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><meta property=\"og:url\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.OGURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 87, Col: 42}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"><meta property=\"og:site_name\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(m.OGSiteName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 88, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"><meta property=\"og:type\" content=\"website\"><meta property=\"og:locale\" content=\"en_US\"><meta name=\"twitter:card\" content=\"summary_large_image\"><meta name=\"twitter:site\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(m.TwitterSite)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 92, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><meta name=\"twitter:creator\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(m.TwitterCreator)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 93, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\"><meta name=\"twitter:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 94, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"><meta name=\"twitter:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 95, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\"><meta name=\"twitter:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(m.TwitterImage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/meta/metadata.templ`, Line: 96, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

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 credentials;

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 @@
DROP TABLE prices;

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 @@
DROP TABLE health;

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,200 @@
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"
silent: true
tasks:
default:
cmds:
- task: migrate
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 -y
migrate:accounts:down:
cmd: wrangler d1 execute USERS_DB --file 001_accounts_table.down.sql --remote -y
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 -y
migrate:credentials:down:
cmd: wrangler d1 execute USERS_DB --file 002_credentials_table.down.sql --remote -y
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 -y
migrate:profiles:down:
cmd: wrangler d1 execute USERS_DB --file 003_profiles_table.down.sql --remote -y
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 -y
migrate:vaults:up:
cmd: wrangler d1 execute USERS_DB --file 004_vaults_table.up.sql --remote -y
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 -y
migrate:assets:down:
cmd: wrangler d1 execute NETWORK_DB --file 005_assets_table.down.sql --remote -y
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 -y
migrate:prices:down:
cmd: wrangler d1 execute NETWORK_DB --file 006_prices_table.down.sql --remote -y
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 -y
migrate:price_conversions:down:
cmd: wrangler d1 execute NETWORK_DB --file 007_price_conversions_table.down.sql --remote -y
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 -y
migrate:blockchains:down:
cmd: wrangler d1 execute NETWORK_DB --file 008_blockchains_table.down.sql --remote -y
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 -y
migrate:services:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 009_services_table.down.sql --remote -y
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 -y
migrate:activities:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 010_activities_table.down.sql --remote -y
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 -y
migrate:health:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 011_health_table.down.sql --remote -y
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 -y
migrate:global_market:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 012_global_market_table.down.sql --remote -y
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 -y
migrate:fear_greed_index:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 013_fear_greed_index_table.down.sql --remote -y
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 -y
migrate:crypto_listings:down:
cmd: wrangler d1 execute ACTIVITY_DB --file 014_crypto_listings_table.down.sql --remote -y

View File

@@ -0,0 +1,6 @@
{
"account": {
"id": "eb37925850388bca807b7fab964c12bb",
"name": "Sonr"
}
}

1
internal/migrations/node_modules/.mf/cf.json generated vendored Normal file
View File

@@ -0,0 +1 @@
{"clientTcpRtt":8,"requestHeaderNames":{},"httpProtocol":"HTTP/1.1","tlsCipher":"AEAD-AES256-GCM-SHA384","continent":"NA","asn":701,"clientAcceptEncoding":"br, gzip, deflate","verifiedBotCategory":"","country":"US","region":"Virginia","tlsClientCiphersSha1":"kXrN3VEKDdzz2cPKTQaKzpxVTxQ=","tlsClientAuth":{"certIssuerDNLegacy":"","certIssuerSKI":"","certSubjectDNRFC2253":"","certSubjectDNLegacy":"","certFingerprintSHA256":"","certNotBefore":"","certSKI":"","certSerial":"","certIssuerDN":"","certVerified":"NONE","certNotAfter":"","certSubjectDN":"","certPresented":"0","certRevoked":"0","certIssuerSerial":"","certIssuerDNRFC2253":"","certFingerprintSHA1":""},"tlsClientRandom":"KHkBe8nH4XNP9wnNS5nCDWBpe+Ha+8+BUuP0iev0P7Q=","tlsExportedAuthenticator":{"clientFinished":"c71857a631b6612f8bdfda376b597ddb0ccf62688fc7f50086006daba82f54c412501557ccfce73754bc550a1e09a6b9","clientHandshake":"8d0a2b64f7b6d0d1c2a77d7535feca90c9703a46c457b4951670146a8b5e2fe89357c6d8666c4e7f864e6814e7bb1d0f","serverHandshake":"429ef59250f50d719b076c2efdf97ecd5d1a50c15fdf979df5894d078793865ff44c7680213365147c44daedbc92bec6","serverFinished":"6e46d6694b01edbbc7d5daa9316565f17fb3a626713c96286d07487a7ddb7482aea03a84971fc74231d848d2f037af41"},"tlsClientHelloLength":"383","colo":"IAD","timezone":"America/New_York","longitude":"-77.53900","latitude":"39.01800","edgeRequestKeepAliveStatus":1,"requestPriority":"","postalCode":"20147","city":"Ashburn","tlsVersion":"TLSv1.3","regionCode":"VA","asOrganization":"Verizon Fios","metroCode":"511","tlsClientExtensionsSha1Le":"u4wtEMFQBY18l3BzHAvORm+KGRw=","tlsClientExtensionsSha1":"1eY97BUYYO8vDaTfHQywB1pcNdM=","botManagement":{"corporateProxy":false,"verifiedBot":false,"jsDetection":{"passed":false},"staticResource":false,"detectionIds":{},"score":99}}

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

@@ -0,0 +1,20 @@
package charts
import "fmt"
type DateValue struct {
Date string
Value int
}
templ AreaChart(data []DateValue) {
for _, d := range data {
<div class="flex flex-col">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-900">${ d.Date }</span>
<span class="text-sm font-medium text-gray-900">${ fmt.Sprintf("%d", d.Value) }</span>
</div>
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
}
}

View File

@@ -0,0 +1,75 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package charts
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
type DateValue struct {
Date string
Value int
}
func AreaChart(data []DateValue) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
for _, d := range data {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-col\"><div class=\"flex justify-between\"><span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(d.Date)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/area_chart.templ`, Line: 14, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> <span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.Value))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/area_chart.templ`, Line: 15, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span></div><div class=\"h-1 bg-gray-200 rounded-full\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,21 @@
package charts
import "fmt"
type KeyValue struct {
Key string
Value int
Color string
}
templ BarChart(data []KeyValue) {
for _, d := range data {
<div class="flex flex-col">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-900">${ d.Key }</span>
<span class="text-sm font-medium text-gray-900">${ fmt.Sprintf("%d", d.Value) }</span>
</div>
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
}
}

View File

@@ -0,0 +1,76 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package charts
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
type KeyValue struct {
Key string
Value int
Color string
}
func BarChart(data []KeyValue) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
for _, d := range data {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-col\"><div class=\"flex justify-between\"><span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(d.Key)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/bar_chart.templ`, Line: 15, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> <span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.Value))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/bar_chart.templ`, Line: 16, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span></div><div class=\"h-1 bg-gray-200 rounded-full\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,106 @@
package charts
import (
"encoding/json"
"time"
)
type CandleData struct {
Open float64
Close float64
High float64
Low float64
Date time.Time
}
// D3 script handle for deduplication
var d3Handle = templ.NewOnceHandle()
// D3 component for loading D3.js
templ D3() {
@d3Handle.Once() {
<script type="module">
window.d3 = d3;
</script>
}
}
// CandleChart component
templ CandleChart(data []CandleData) {
@D3()
<div id="candleChart" class="@container relative">
<div
class="relative h-72 w-full"
style="--marginTop: 10px; --marginRight: 60px; --marginBottom: 56px; --marginLeft: 30px;"
></div>
</div>
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
// Convert Go data to JavaScript
// Declare the chart dimensions and margins.
const width = 640;
const height = 400;
const marginTop = 20;
const marginRight = 20;
const marginBottom = 30;
const marginLeft = 40;
// Declare the x (horizontal position) scale.
const x = d3.scaleUtc()
.domain([new Date("2023-01-01"), new Date("2024-01-01")])
.range([marginLeft, width - marginRight]);
// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - marginBottom, marginTop]);
// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
// Add the x-axis.
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x));
// Add the y-axis.
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y));
// Append the SVG element.
container.append(svg.node());
</script>
}
// formatDataForJS converts the Go data structure to a JavaScript-compatible JSON string
func formatDataForJS(data []CandleData) string {
type jsData struct {
Date string `json:"date"`
Open float64 `json:"open"`
Close float64 `json:"close"`
High float64 `json:"high"`
Low float64 `json:"low"`
}
jsDataArray := make([]jsData, len(data))
for i, d := range data {
jsDataArray[i] = jsData{
Date: d.Date.Format(time.RFC3339),
Open: d.Open,
Close: d.Close,
High: d.High,
Low: d.Low,
}
}
jsonBytes, err := json.Marshal(jsDataArray)
if err != nil {
return "[]"
}
return string(jsonBytes)
}

View File

@@ -0,0 +1,137 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package charts
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"encoding/json"
"time"
)
type CandleData struct {
Open float64
Close float64
High float64
Low float64
Date time.Time
}
// D3 script handle for deduplication
var d3Handle = templ.NewOnceHandle()
// D3 component for loading D3.js
func D3() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script type=\"module\">\n \n window.d3 = d3;\n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = d3Handle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// CandleChart component
func CandleChart(data []CandleData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = D3().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div id=\"candleChart\" class=\"@container relative\"><div class=\"relative h-72 w-full\" style=\"--marginTop: 10px; --marginRight: 60px; --marginBottom: 56px; --marginLeft: 30px;\"></div></div><script type=\"module\">\n\t import * as d3 from \"https://cdn.jsdelivr.net/npm/d3@7/+esm\";\n // Convert Go data to JavaScript\n \n // Declare the chart dimensions and margins.\n const width = 640;\n const height = 400;\n const marginTop = 20;\n const marginRight = 20;\n const marginBottom = 30;\n const marginLeft = 40;\n\n // Declare the x (horizontal position) scale.\n const x = d3.scaleUtc()\n .domain([new Date(\"2023-01-01\"), new Date(\"2024-01-01\")])\n .range([marginLeft, width - marginRight]);\n\n // Declare the y (vertical position) scale.\n const y = d3.scaleLinear()\n .domain([0, 100])\n .range([height - marginBottom, marginTop]);\n\n // Create the SVG container.\n const svg = d3.create(\"svg\")\n .attr(\"width\", width)\n .attr(\"height\", height);\n\n // Add the x-axis.\n svg.append(\"g\")\n .attr(\"transform\", `translate(0,${height - marginBottom})`)\n .call(d3.axisBottom(x));\n\n // Add the y-axis.\n svg.append(\"g\")\n .attr(\"transform\", `translate(${marginLeft},0)`)\n .call(d3.axisLeft(y));\n\n // Append the SVG element.\n container.append(svg.node()); \n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// formatDataForJS converts the Go data structure to a JavaScript-compatible JSON string
func formatDataForJS(data []CandleData) string {
type jsData struct {
Date string `json:"date"`
Open float64 `json:"open"`
Close float64 `json:"close"`
High float64 `json:"high"`
Low float64 `json:"low"`
}
jsDataArray := make([]jsData, len(data))
for i, d := range data {
jsDataArray[i] = jsData{
Date: d.Date.Format(time.RFC3339),
Open: d.Open,
Close: d.Close,
High: d.High,
Low: d.Low,
}
}
jsonBytes, err := json.Marshal(jsDataArray)
if err != nil {
return "[]"
}
return string(jsonBytes)
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,15 @@
package charts
import "fmt"
templ LineChart(data []DateValue) {
for _, d := range data {
<div class="flex flex-col">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-900">${ d.Date }</span>
<span class="text-sm font-medium text-gray-900">${ fmt.Sprintf("%d", d.Value) }</span>
</div>
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
}
}

View File

@@ -0,0 +1,70 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package charts
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
func LineChart(data []DateValue) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
for _, d := range data {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-col\"><div class=\"flex justify-between\"><span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(d.Date)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/line_chart.templ`, Line: 9, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> <span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.Value))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/line_chart.templ`, Line: 10, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span></div><div class=\"h-1 bg-gray-200 rounded-full\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,22 @@
package charts
import "fmt"
type CategoryValue struct {
Category string
Value int
ColorFrom string
ColorTo string
}
templ PieChart(data []CategoryValue) {
for _, d := range data {
<div class="flex flex-col">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-900">${ d.Category }</span>
<span class="text-sm font-medium text-gray-900">${ fmt.Sprintf("%d", d.Value) }</span>
</div>
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
}
}

View File

@@ -0,0 +1,77 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package charts
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
type CategoryValue struct {
Category string
Value int
ColorFrom string
ColorTo string
}
func PieChart(data []CategoryValue) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
for _, d := range data {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-col\"><div class=\"flex justify-between\"><span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(d.Category)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/pie_chart.templ`, Line: 16, Col: 65}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> <span class=\"text-sm font-medium text-gray-900\">$")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.Value))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/charts/pie_chart.templ`, Line: 17, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span></div><div class=\"h-1 bg-gray-200 rounded-full\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

113
internal/ui/layout.templ Normal file
View File

@@ -0,0 +1,113 @@
package ui
// Body is a component that renders the body tag
templ Body() {
<body>
{ children... }
</body>
}
// Head is a component that renders the head of the document
templ Head() {
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@ApexCharts()
@Helia()
@Dexie()
@Htmx()
@Tailwind()
@Shoelace()
@DefaultStyles()
{ children... }
</head>
}
// HTML is a component that renders the html tag
templ HTML() {
<!DOCTYPE html>
<html lang="en">
{ children... }
</html>
}
// Columns is a component that renders a responsive flex container that stacks on mobile
templ Columns() {
<div class="flex flex-col h-full w-full gap-4 md:gap-6 md:flex-row md:flex-wrap">
{ children... }
</div>
}
// Container is a component that renders a full screen container
templ Container() {
<div id="container" class="flex fixed inset-0 z-[99] w-screen min-h-screen">
<div class="relative flex flex-wrap items-center w-full min-h-full px-4 py-6 sm:px-6 md:px-8">
<div class="relative w-full max-w-screen-lg mx-auto">
<div class="flex flex-col items-center justify-center min-h-full gap-4">
{ children... }
</div>
</div>
</div>
</div>
}
// Tailwind css dependencies
templ Tailwind() {
@tailwindHandle.Once() {
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
}
}
// Nav is a component that renders the navigation bar
templ Nav() {
<nav class="absolute inset-x-0 top-0 z-[100] flex h-16 w-full items-center justify-between px-4 py-4 sm:px-6 md:px-8">
{ children... }
</nav>
}
// NavCTA is a component that renders a call to action button
templ NavCTA(href string, text string) {
<sl-button type="primary" href={ href }>{ text }</sl-button>
}
// NavItem is a component that renders a navigation item
templ NavItem(href string, text string) {
<sl-button type="text" href={ href }>{ text }</sl-button>
}
// NavLogo is a component that renders a logo
templ NavLogo(title string) {
<a href="/" class="flex items-center justify-center gap-1.5 px-2 py-2">
{ children... }
<span class="text-xl font-bold pb-1.5">{ title }</span>
</a>
}
// NavLeft is a component that renders the left side of the navigation bar
templ NavLeft() {
<div class="flex items-center gap-4">
{ children... }
</div>
}
templ NavRight() {
<div class="flex items-center gap-4">
{ children... }
</div>
}
// Rows is a component that renders a responsive flex container that wraps on mobile
templ Rows() {
<div class="flex flex-col w-full gap-3 sm:flex-row sm:flex-wrap sm:gap-4">
{ children... }
</div>
}
templ Separator(text string) {
<div class="relative py-6">
<div class="absolute inset-0 flex items-center"><span class="w-full border-t"></span></div>
<div class="relative flex justify-center text-xs uppercase">
<span class="px-2 text-neutral-500">{ text }</span>
</div>
</div>
}

633
internal/ui/layout_templ.go Normal file
View File

@@ -0,0 +1,633 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package ui
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
// Body is a component that renders the body tag
func Body() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Head is a component that renders the head of the document
func Head() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = ApexCharts().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Helia().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Dexie().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Htmx().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Tailwind().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Shoelace().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = DefaultStyles().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var2.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// HTML is a component that renders the html tag
func HTML() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<!doctype html><html lang=\"en\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var3.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Columns is a component that renders a responsive flex container that stacks on mobile
func Columns() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
if templ_7745c5c3_Var4 == nil {
templ_7745c5c3_Var4 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"flex flex-col h-full w-full gap-4 md:gap-6 md:flex-row md:flex-wrap\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var4.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Container is a component that renders a full screen container
func Container() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div id=\"container\" class=\"flex fixed inset-0 z-[99] w-screen min-h-screen\"><div class=\"relative flex flex-wrap items-center w-full min-h-full px-4 py-6 sm:px-6 md:px-8\"><div class=\"relative w-full max-w-screen-lg mx-auto\"><div class=\"flex flex-col items-center justify-center min-h-full gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var5.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Tailwind css dependencies
func Tailwind() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<script src=\"https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = tailwindHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Nav is a component that renders the navigation bar
func Nav() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<nav class=\"absolute inset-x-0 top-0 z-[100] flex h-16 w-full items-center justify-between px-4 py-4 sm:px-6 md:px-8\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var8.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// NavCTA is a component that renders a call to action button
func NavCTA(href string, text string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
if templ_7745c5c3_Var9 == nil {
templ_7745c5c3_Var9 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<sl-button type=\"primary\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(href)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 70, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 70, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// NavItem is a component that renders a navigation item
func NavItem(href string, text string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
if templ_7745c5c3_Var12 == nil {
templ_7745c5c3_Var12 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<sl-button type=\"text\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(href)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 75, Col: 35}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 75, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// NavLogo is a component that renders a logo
func NavLogo(title string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var15 := templ.GetChildren(ctx)
if templ_7745c5c3_Var15 == nil {
templ_7745c5c3_Var15 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<a href=\"/\" class=\"flex items-center justify-center gap-1.5 px-2 py-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var15.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<span class=\"text-xl font-bold pb-1.5\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 82, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</span></a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// NavLeft is a component that renders the left side of the navigation bar
func NavLeft() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var17 := templ.GetChildren(ctx)
if templ_7745c5c3_Var17 == nil {
templ_7745c5c3_Var17 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div class=\"flex items-center gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var17.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func NavRight() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var18 := templ.GetChildren(ctx)
if templ_7745c5c3_Var18 == nil {
templ_7745c5c3_Var18 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"flex items-center gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var18.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Rows is a component that renders a responsive flex container that wraps on mobile
func Rows() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var19 := templ.GetChildren(ctx)
if templ_7745c5c3_Var19 == nil {
templ_7745c5c3_Var19 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<div class=\"flex flex-col w-full gap-3 sm:flex-row sm:flex-wrap sm:gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var19.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func Separator(text string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var20 := templ.GetChildren(ctx)
if templ_7745c5c3_Var20 == nil {
templ_7745c5c3_Var20 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"relative py-6\"><div class=\"absolute inset-0 flex items-center\"><span class=\"w-full border-t\"></span></div><div class=\"relative flex justify-center text-xs uppercase\"><span class=\"px-2 text-neutral-500\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/layout.templ`, Line: 110, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</span></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

139
internal/ui/providers.templ Normal file
View File

@@ -0,0 +1,139 @@
package ui
import "fmt"
var (
apexChartsHandle = templ.NewOnceHandle()
d3Handle = templ.NewOnceHandle()
dexieHandle = templ.NewOnceHandle()
heliaHandle = templ.NewOnceHandle()
htmxHandle = templ.NewOnceHandle()
tailwindHandle = templ.NewOnceHandle()
)
// ApexCharts is a component that renders the ApexCharts.js library
templ ApexCharts() {
@apexChartsHandle.Once() {
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
}
}
// d3 is a component that renders the D3.js library
templ D3() {
@d3Handle.Once() {
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
</script>
}
}
// dexie is a component that renders the Dexie.js library
templ Dexie() {
@dexieHandle.Once() {
<script src={ jsDelivrURL("dexie", "4.0.10", "dist/dexie.min.js") }></script>
<script src={ jsDelivrURL("dexie-export-import", "4.1.4", "dist/dexie-export-import.min.js") }></script>
}
}
// In package deps
templ Helia() {
@heliaHandle.Once() {
<script src="https://unpkg.com/@helia/unixfs/dist/index.min.js"></script>
<script src="https://unpkg.com/blockstore-core/dist/index.min.js"></script>
<script src="https://unpkg.com/datastore-core/dist/index.min.js"></script>
<script src="https://unpkg.com/helia/dist/index.min.js"></script>
<script>
// Time formatting helper
function ms2TimeString(a) {
const k = a % 1e3
const s = a / 1e3 % 60 | 0
const m = a / 6e4 % 60 | 0
const h = a / 36e5 % 24 | 0
return (h ? (h < 10 ? '0' + h : h) + ':' : '00:') +
(m < 10 ? 0 : '') + m + ':' +
(s < 10 ? 0 : '') + s + ':' +
(k < 100 ? k < 10 ? '00' : 0 : '') + k
}
// Log management
const getLogLineEl = (msg) => {
const logLine = document.createElement('span')
logLine.innerHTML = `${ms2TimeString(performance.now())} - ${msg}`
return logLine
}
const addToLog = (msg) => {
const logEl = document.getElementById('runningLog')
if (logEl) {
logEl.appendChild(getLogLineEl(msg))
logEl.appendChild(document.createElement('br'))
}
}
// Peer management
window.discoveredPeers = new Map()
const updateConnectedPeers = () => {
if (!window.helia || !window.helia.libp2p) return
const peers = window.helia.libp2p.getPeers()
const connectedPeerCountEl = document.getElementById('connectedPeerCount')
const connectedPeersListEl = document.getElementById('connectedPeersList')
if (connectedPeerCountEl) {
connectedPeerCountEl.innerHTML = peers.length
}
if (connectedPeersListEl) {
connectedPeersListEl.innerHTML = ''
for (const peer of peers) {
const peerEl = document.createElement('li')
peerEl.innerText = peer.toString()
connectedPeersListEl.appendChild(peerEl)
}
}
}
const updateDiscoveredPeers = () => {
const discoveredPeerCountEl = document.getElementById('discoveredPeerCount')
if (discoveredPeerCountEl) {
discoveredPeerCountEl.innerHTML = window.discoveredPeers.size
}
}
// Helia node instantiation
let heliaInstance = null
window.instantiateHeliaNode = async () => {
// application-specific data lives in the datastore
const datastore = new DatastoreCore.MemoryDatastore()
const blockstore = new BlockstoreCore.MemoryBlockstore()
if (heliaInstance != null) {
return heliaInstance
}
heliaInstance = await Helia.createHelia({
datastore,
blockstore
})
addToLog('Created Helia instance')
return heliaInstance
}
</script>
}
}
// Htmx is a component that renders the Htmx.js library
templ Htmx() {
@htmxHandle.Once() {
<script src={ jsDelivrURL("htmx.org", "1.9.12", "dist/htmx.min.js") }></script>
<script src={ jsDelivrURL("htmx-ext-include-vals", "2.0.0", "include-vals.min.js") }></script>
<script src={ jsDelivrURL("htmx-ext-path-params", "2.0.0", "path-params.min.js") }></script>
<script src={ jsDelivrURL("htmx-ext-alpine-morph", "2.0.0", "alpine-morph.min.js") }></script>
<script src={ jsDelivrURL("htmx-ext-sse", "2.2.2", "sse.min.js") }></script>
<script src={ jsDelivrURL("htmx-ext-ws", "2.0.2", "ws.min.js") }></script>
}
}
// jsDelivrURL returns the URL of a package on jsDelivr
func jsDelivrURL(pkg string, version string, path string) string {
return fmt.Sprintf("https://cdn.jsdelivr.net/npm/%s/%s/%s", pkg, version, path)
}

View File

@@ -0,0 +1,371 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package ui
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
var (
apexChartsHandle = templ.NewOnceHandle()
d3Handle = templ.NewOnceHandle()
dexieHandle = templ.NewOnceHandle()
heliaHandle = templ.NewOnceHandle()
htmxHandle = templ.NewOnceHandle()
tailwindHandle = templ.NewOnceHandle()
)
// ApexCharts is a component that renders the ApexCharts.js library
func ApexCharts() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script src=\"https://cdn.jsdelivr.net/npm/apexcharts\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = apexChartsHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// d3 is a component that renders the D3.js library
func D3() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<script type=\"module\">\n import * as d3 from \"https://cdn.jsdelivr.net/npm/d3@7/+esm\";\n\t </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = d3Handle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// dexie is a component that renders the Dexie.js library
func Dexie() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("dexie", "4.0.10", "dist/dexie.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 33, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("dexie-export-import", "4.1.4", "dist/dexie-export-import.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 34, Col: 94}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = dexieHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// In package deps
func Helia() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
if templ_7745c5c3_Var9 == nil {
templ_7745c5c3_Var9 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<script src=\"https://unpkg.com/@helia/unixfs/dist/index.min.js\"></script> <script src=\"https://unpkg.com/blockstore-core/dist/index.min.js\"></script> <script src=\"https://unpkg.com/datastore-core/dist/index.min.js\"></script> <script src=\"https://unpkg.com/helia/dist/index.min.js\"></script> <script>\n // Time formatting helper\n function ms2TimeString(a) {\n const k = a % 1e3\n const s = a / 1e3 % 60 | 0\n const m = a / 6e4 % 60 | 0\n const h = a / 36e5 % 24 | 0\n\n return (h ? (h < 10 ? '0' + h : h) + ':' : '00:') +\n (m < 10 ? 0 : '') + m + ':' +\n (s < 10 ? 0 : '') + s + ':' +\n (k < 100 ? k < 10 ? '00' : 0 : '') + k\n }\n\n // Log management\n const getLogLineEl = (msg) => {\n const logLine = document.createElement('span')\n logLine.innerHTML = `${ms2TimeString(performance.now())} - ${msg}`\n return logLine\n }\n \n const addToLog = (msg) => {\n const logEl = document.getElementById('runningLog')\n if (logEl) {\n logEl.appendChild(getLogLineEl(msg))\n logEl.appendChild(document.createElement('br'))\n }\n }\n\n // Peer management\n window.discoveredPeers = new Map()\n const updateConnectedPeers = () => {\n if (!window.helia || !window.helia.libp2p) return\n \n const peers = window.helia.libp2p.getPeers()\n const connectedPeerCountEl = document.getElementById('connectedPeerCount')\n const connectedPeersListEl = document.getElementById('connectedPeersList')\n \n if (connectedPeerCountEl) {\n connectedPeerCountEl.innerHTML = peers.length\n }\n \n if (connectedPeersListEl) {\n connectedPeersListEl.innerHTML = ''\n \n for (const peer of peers) {\n const peerEl = document.createElement('li')\n peerEl.innerText = peer.toString()\n connectedPeersListEl.appendChild(peerEl)\n }\n }\n }\n const updateDiscoveredPeers = () => {\n const discoveredPeerCountEl = document.getElementById('discoveredPeerCount')\n if (discoveredPeerCountEl) {\n discoveredPeerCountEl.innerHTML = window.discoveredPeers.size\n }\n }\n // Helia node instantiation\n let heliaInstance = null\n window.instantiateHeliaNode = async () => {\n // application-specific data lives in the datastore\n const datastore = new DatastoreCore.MemoryDatastore()\n const blockstore = new BlockstoreCore.MemoryBlockstore()\n\n if (heliaInstance != null) {\n return heliaInstance\n }\n heliaInstance = await Helia.createHelia({\n datastore,\n blockstore\n })\n addToLog('Created Helia instance')\n return heliaInstance\n }\n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = heliaHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Htmx is a component that renders the Htmx.js library
func Htmx() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var11 := templ.GetChildren(ctx)
if templ_7745c5c3_Var11 == nil {
templ_7745c5c3_Var11 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx.org", "1.9.12", "dist/htmx.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 127, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx-ext-include-vals", "2.0.0", "include-vals.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 128, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx-ext-path-params", "2.0.0", "path-params.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 129, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx-ext-alpine-morph", "2.0.0", "alpine-morph.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 130, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx-ext-sse", "2.2.2", "sse.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 131, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\"></script> <script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(jsDelivrURL("htmx-ext-ws", "2.0.2", "ws.min.js"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/providers.templ`, Line: 132, Col: 64}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = htmxHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// jsDelivrURL returns the URL of a package on jsDelivr
func jsDelivrURL(pkg string, version string, path string) string {
return fmt.Sprintf("https://cdn.jsdelivr.net/npm/%s/%s/%s", pkg, version, path)
}
var _ = templruntime.GeneratedTemplate

54
internal/ui/styles.templ Normal file
View File

@@ -0,0 +1,54 @@
package ui
templ DefaultStyles() {
<style>
@theme {
--color-primary: #17c2ff;
}
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
.slide-it {
view-transition-name: slide-it;
}
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
</style>
}
// Shoelace dependencies
templ Shoelace() {
<link
rel="stylesheet"
media="(prefers-color-scheme:light)"
href="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/light.css"
/>
<link
rel="stylesheet"
media="(prefers-color-scheme:dark)"
href="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/dark.css"
onload="document.documentElement.classList.add('sl-theme-dark');"
/>
<script type="module" src="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/shoelace-autoloader.js"></script>
}

View File

@@ -0,0 +1,70 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package ui
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func DefaultStyles() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<style>\n\t @theme {\n --color-primary: #17c2ff;\n }\n\n \t\t@keyframes fade-in {\n \t\tfrom { opacity: 0; }\n \t\t}\n\n \t\t@keyframes fade-out {\n \t\tto { opacity: 0; }\n \t\t}\n\n \t\t@keyframes slide-from-right {\n \t\tfrom { transform: translateX(90px); }\n \t\t}\n\n \t\t@keyframes slide-to-left {\n \t\tto { transform: translateX(-90px); }\n \t\t}\n\n \t\t.slide-it {\n \t\tview-transition-name: slide-it;\n \t\t}\n\n \t\t::view-transition-old(slide-it) {\n \t\tanimation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n \t\t600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n \t\t}\n \t\t::view-transition-new(slide-it) {\n \t\tanimation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,\n \t\t600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n \t\t}\n\t\t</style>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Shoelace dependencies
func Shoelace() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<link rel=\"stylesheet\" media=\"(prefers-color-scheme:light)\" href=\"https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/light.css\"><link rel=\"stylesheet\" media=\"(prefers-color-scheme:dark)\" href=\"https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/dark.css\" onload=\"document.documentElement.classList.add(&#39;sl-theme-dark&#39;);\"><script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/shoelace-autoloader.js\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

72
main.go Normal file
View File

@@ -0,0 +1,72 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"context"
"fmt"
"net/http"
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/config"
"github.com/sonr-io/motr/middleware/database"
"github.com/sonr-io/motr/middleware/kvstore"
"github.com/sonr-io/motr/middleware/session"
"github.com/sonr-io/motr/middleware/webauthn"
"github.com/syumai/workers"
"github.com/syumai/workers/cloudflare/cron"
_ "github.com/syumai/workers/cloudflare/d1"
)
// ╭──────────────────────────────────────────────────╮
// │ Initialization │
// ╰──────────────────────────────────────────────────╯
// Setup the HTTP handler
func loadHandler() http.Handler {
e := echo.New()
e.Use(
session.Middleware(),
database.Middleware(),
kvstore.Middleware(),
webauthn.Middleware(),
)
config.RegisterViews(e)
config.RegisterPartials(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
cron.ScheduleTaskNonBlock(t)
workers.ServeNonBlock(e)
workers.Ready()
// Block until handler/task is done
select {
case <-workers.Done():
case <-cron.Done():
}
}

Some files were not shown because too many files have changed in this diff Show More