Refactor/orm (#7)

* feat/refactor-motr

* feat/refactor-motr

* feat: introduce Cloudflare Workers gateway application

* chore: migrate build system from Makefile to Taskfile for improved automation

* feat: streamline task management with Taskfile consolidation

* feat: consolidate build and install tasks for simplified workflows

* <no value>

* feat: enable publishing for core packages

* feat/refactor-motr

* feat: integrate updated crypto library for enhanced security

* ci: Configure CI/CD to build and test WASM signer

* refactor: streamline build process using npm scripts

* fix: Correct template file paths for accurate error reporting

* refactor: standardize templ file paths for improved maintainability

* chore: remove package documentation

* build: exclude documentation artifacts from version control

* feat: serve static assets from cloudflare worker

* feat: introduce Motr controller service

* refactor: move UI components to separate ui module

* refactor: move resolver middleware to top-level middleware directory

* feat: introduce modular middleware architecture

* refactor: improve separation of concerns by relocating endpoint definitions

* build: simplify codebase by deleting unused info types

* refactor: decouple middleware configurations

* feat: integrate request middleware for enhanced processing

* feat: implement register and login pages

* feat: integrate WASM signer for enhanced security

* refactor: move type definitions to models package

* refactor: rename ipfs middleware to vault middleware

* feat: Add Dockerfile for controller service

* feat: Add Dockerfile for controller with multi-stage build

* chore: Update Golang version to 1.24.2 in Dockerfile

* feat: introduce Docker support for application deployment

* feat: Implement WASM-based signing service

* feat: migrate build system to Taskfile for improved automation

* feat: enable docker-based development for controller and resolver

* feat: Add docker-compose services for resolver and controller workers

* refactor: streamline build and deployment processes with Devbox

* chore: standardize development environment with Devbox

* chore: standardize container entrypoint for improved consistency

* feat: introduce docker-compose setup for local development

* feat: remove initial placeholder code

* refactor: restructure project modules for improved organization

* feat: integrate motr UI library for enhanced components

* chore: upgrade motr dependency to v0.0.3

* refactor: restructure project layout for improved modularity

* refactor: consolidate data models and options into  directory

* feat: integrate sqlc for database interaction

* feat: integrate D1 database for improved data management

* refactor: improve naming consistency across project

* feat: enhance context with HTTP header utilities

* refactor: restructure project layout for improved maintainability

* refactor: centralize rendering logic into middleware

* chore: update motr dependency to v0.0.5 to address data consistency issues

* feat: consolidate handler logic into root

* refactor: relocate handlers and middleware to internal packages

* chore: update  dependency to v0.9.0

* refactor: Improve code structure and add comprehensive documentation for WebAssembly signer module

* feat: implement WASM-based signing functionality

* chore: remove build system configuration

* feat: integrate D1 database for persistent data storage

* feat: enable D1 database integration for Cloudflare Workers

* feat: enhance task execution with docker-compose integration

* refactor: centralize database queries and models

* refactor: improve sqlc code generation and project structure

* docs: Update README with Docker support and new project architecture

* refactor: centralize Sonr configuration via middleware

* chore: improve build task definitions in Taskfile

* chore: remove docker deployment configuration

* feat: upgrade crypto module to v0.11.0

* refactor: migrate to a configuration-driven architecture

* refactor: inject database common queries into index handler

* feat: streamline worker initialization

* refactor: standardize package versioning process

* build: prepare vault package for public release

* feat: enable WASM-based vault signing with durable objects

* feat: introduce Vault service for enhanced security

* feat: upgrade crypto library to v0.12.1 for enhanced security features

* build: update middleware build constraints

* feat: introduce struct-based handlers for improved organization

* feat: centralize database configuration

* build: update database seeding to target remote instance

* feat: enhance asset handling with shared coin type

* feat: decouple build process from database initialization

* refactor: move base UI components to a dedicated directory

* refactor: improve component structure for enhanced maintainability

* refactor: rename binding for clarity and consistency

* feat: introduce development task and environment configurations

* feat: introduce mprocs for simplified local development

* <no value>

* refactor: rename Vault DB to Controller DB for clarity

* refactor: simplify configuration loading and database connections

* feat: introduce MotrMode configuration to differentiate service roles

* refactor: restructure base components and consolidate metadata handling

* feat: introduce session context middleware for request handling

* feat: enhance task management with database migration support

* feat: Add database controller middleware and context retrieval methods

* refactor: Improve session context handling and add full database controller middleware

* feat: integrate Helia and Extism for Vault durable object

* refactor: rename resolver to frontend

* feat: streamline infrastructure and data management

* refactor: improve task management and dependency loading

* fix: remove legacy fetch handler from vault worker

* feat: integrate WebAuthn for secure authentication

* build: add hx-trigger for enhanced interaction

* refactor: centralize session ID handling in middleware

* refactor: simplify session context initialization

* feat/split workers (#6)

* refactor: rename claim route to register for clarity

* refactor: rename demo view to dashboard view

* feat: automate build and publish process

* refactor: unify database queries and models for vault operations

* feat: implement WASM compatible server and routing

* feat: introduce controller-based routing

* feat: implement passkey-based authentication

* feat: implement Cloudflare cache middleware for improved performance

* refactor: move ui components to ui package

* feat: add handler functions for login and registration

* refactor: centralize route registration logic

* feat: implement user registration and login with passkey support

* refactor: restructure view components for improved organization

* feat: enable handle-based authentication and session management

* refactor: improve config access and remove unused dependencies

* refactor: restructure middleware and configuration for clarity

* refactor: Remove external libraries

* docs: update architecture to reflect removal of WASM build

* refactor: centralize metadata and coin info types

* refactor: move auth types to auth package

* refactor: remove session management from vault service

* refactor: reorganize UI components and middleware for clarity

* refactor: move UI components to internal directory

* feat: add session expiry configuration

* refactor: streamline Taskfile and remove deprecated options

* feat: introduce account entities and UI components

* refactor: streamline asset representation with entity interfaces

* feat: introduce asset pricing and market data

* feat: enhance radar with session and cache middleware

* fix: update dependencies and resolve peer dependency conflicts

* feat: add blockchain entity and data model

* feat: introduce entity-specific query interfaces

* feat: Implement transaction creation, signing, and broadcasting methods

* feat: implement durable object vault with RPC and IPFS support

* refactor: align protobuf imports with updated cosm-orc package

* feat: simplify auth components by removing options

* feat: enhance data models for comprehensive crypto analysis

* feat: implement account lookup by handle

* refactor: simplify account card rendering

* feat: enhance UI rendering with middleware

* feat: remove helia file operations

* build: scaffold navigation component with templ

* feat: introduce UI layout components and dependency management

* feat: remove unused assets for templated html

* feat: implement authentication flow with WebAuthn and handle registration

* feat: remove radar cmd

* feat: enhance application architecture with module-based structure

* refactor: relocate config and middleware to internal packages

* feat: integrate session management into landing page

* feat: improve landing page handler setup and session handling

* feat: streamline initial setup and configuration process

* chore: reorganize project layout for clarity and maintainability

* chore: reorganize project structure by moving workers to 'cmd'

* refactor/orm

* feat: enhance task automation with root directory task

* refactor: relocate chart components to dedicated directory
This commit is contained in:
2025-05-09 12:51:47 -04:00
committed by GitHub
parent f6510fb5d2
commit 784a8a2c09
186 changed files with 23452 additions and 4752 deletions

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env bash
set -e

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json",
"packages": ["go@latest", "cargo@latest", "uv@latest", "bun@latest"],
"env": {
"PATH": "$HOME/.cargo/bin:$HOME/go/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH",
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GOPATH": "$HOME/go",
"GOBIN": "$GOPATH/bin",
"GHQ_ROOT": "$CLONEDIR"
},
"shell": {
"init_hook": [],
"scripts": {
"test": ["echo \"Error: no test specified\" && exit 1"]
}
}
}

View File

@@ -1,23 +0,0 @@
version: "0.5"
processes:
IPFS:
namespace: testnet
command: "ipfs daemon --mount"
ready_log_line: "Daemon is ready"
Sonr:
namespace: testnet
command: "task sonrd:start"
depends_on:
IPFS:
condition: process_log_ready
Hway:
namespace: testnet
command: "hway"
depends_on:
IPFS:
condition: process_log_ready
Sonr:
condition: process_started

14
.gitignore vendored
View File

@@ -1,10 +1,19 @@
.DS_Store
.tmp*
go.work
.aider*
.vscode
.idea
bin
tmp
dist
node_modules
deploy/conf
configs/logs.json
go.work
go.work.sum
!.taskfile.yml
!.taskfile.dist.yml
# Aider related generated files
.aider-context
@@ -99,19 +108,18 @@ cmd/gateway/node_modules
pkg/nebula/node_modules
configs/logs.json
mprocs.yaml
!devbox.lock
!buf.lock
.air.toml
mprocs.yaml
mprocs.log
tools-stamp
deploy/conf
*.aiderscript
*.wasm
interchaintest-downloader
.haptic
packages/vault/.parcel-cache
packages/vault/.parcel-cache

View File

@@ -1,26 +0,0 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
version: 2
project_name: motr
builds:
- id: motr
binary: app
goos:
- js
goarch:
- wasm
release:
github:
owner: sonr-io
name: motr
name_template: "{{ .Tag }} | {{ .Env.RELEASE_DATE }}"
draft: false
replace_existing_draft: true
replace_existing_artifacts: true
extra_files:
- glob: ./README*
announce:
telegram:
enabled: true
chat_id: -1002222617755

115
README.md
View File

@@ -11,66 +11,95 @@ Motr is Sonr's decentralized vault and identity management system. It provides s
- **Progressive Web App**: Works online and offline with service worker integration
- **WASM Architecture**: Core functionality compiled to WebAssembly for cross-platform compatibility
- **Local-First Design**: Data stored locally with sync capabilities to the Sonr blockchain
- **Containerized Deployment**: Docker-based deployment for all components
## Installation
### Standard Installation
```bash
git clone https://github.com/sonr-io/motr.git
cd motr
go mod tidy
```
### Docker Installation
```bash
git clone https://github.com/sonr-io/motr.git
cd motr
docker-compose up -d
```
## Usage
### Run Using Docker
The simplest way to run the full Motr system is with Docker Compose:
```bash
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
```
### Run as a Local Server
```go
package main
import (
"database/sql"
"log"
"github.com/sonr-io/motr/app"
"github.com/sonr-io/motr/pkg/models"
"github.com/sonr-io/motr/pkg/types"
"database/sql"
"log"
"github.com/sonr-io/motr/app"
"github.com/sonr-io/motr/pkg/models"
"github.com/sonr-io/motr/pkg/types"
)
func main() {
dbq, err := setupDatabase()
if err != nil {
log.Fatal(err)
}
config := &types.Config{
MotrToken: "your-token",
SonrChainID: "sonr-testnet-1",
// Other configuration options
}
vault, err := app.New(config, dbq)
if err != nil {
log.Fatal(err)
}
// Start the server
vault.Start(":8080")
dbq, err := setupDatabase()
if err != nil {
log.Fatal(err)
}
config := &types.Config{
MotrToken: "your-token",
SonrChainID: "sonr-testnet-1",
// Other configuration options
}
vault, err := app.New(config, dbq)
if err != nil {
log.Fatal(err)
}
// Start the server
vault.Start(":8080")
}
func setupDatabase() (*models.Queries, error) {
// Initialize your database connection
// ...
// Initialize your database connection
// ...
}
```
### Compile to WebAssembly
```sh
# Build the vault application as WASM
GOOS=js GOARCH=wasm go build -o public/app.wasm ./cmd/vault/main.go
# Build the signer as WASM
GOOS=js GOARCH=wasm go build -o build/signer.wasm ./cmd/signer/main.go
# Build the proxy application for Cloudflare Workers
GOOS=js GOARCH=wasm go build -o workers/proxy.wasm ./cmd/proxy/main.go
# Build the controller application as WASM
GOOS=js GOARCH=wasm go build -o build/controller.wasm ./controller/main.go
# Build the resolver application for Cloudflare Workers
GOOS=js GOARCH=wasm go build -o build/resolver.wasm ./resolver/main.go
```
### Progressive Web App Integration
@@ -86,18 +115,38 @@ Motr can be integrated into progressive web applications, providing:
Motr consists of several components:
- **Echo Server**: REST API for authentication and account management
- **WASM Runtime**: Core logic compiled to WebAssembly
- **Controller**: Manages WebAuthn credential creation and verification
- **Resolver**: Handles name resolution and identity lookups
- **Signer**: WebAssembly-based cryptographic operations for secure signing
- **Service Worker**: Handles offline capabilities and request caching
- **IndexedDB Storage**: Local data persistence
- **Sonr Blockchain Integration**: Identity verification and data synchronization
### Component Details
1. **Controller**
- Manages user credentials and authentication
- Integrates with WebAuthn for credential storage
- Containerized for easy deployment
2. **Resolver**
- Resolves Sonr names to addresses and profiles
- Serves as a gateway to the Sonr network
- Implemented as a Cloudflare Worker
3. **Signer**
- Secure cryptographic operations
- WebAssembly-based for cross-platform compatibility
- Handles key management and signatures
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
[MIT](LICENSE)
[MIT](LICENSE)
Copyright (c) 2024, Sonr Labs, Inc.

View File

@@ -1,66 +1,120 @@
# yaml-language-server: $schema=https://json.schemastore.org/taskfile
#yaml-language-server: $schema=https://json.schemastore.org/taskfile
version: "3"
silent: true
# vars:
# VERSION:
# sh: git describe --tags
# COMMIT:
# sh: git log -1 --format='%H'
#
vars:
ROOT_DIR:
sh: git rev-parse --show-toplevel
tasks:
default:
clean:
desc: Remove build artifacts
cmds:
- task: gen:templ
- task: gen:sqlc
- task: build
- task: tidy:vault
- task: tidy:front
- task: tidy:root
gen:templ:
desc: Generate code
sources:
- "**/*.templ"
generates:
- "**/*_templ.go"
deploy:
desc: Deploy all
cmds:
- templ generate
gen:sqlc:
desc: Generate code
sources:
- x/**/model/*.sql
generates:
- x/**/model/*.go
cmds:
- sqlc generate -f "x/identity/model/sqlc.yaml"
- sqlc generate -f "x/portfolio/model/sqlc.yaml"
- sqlc generate -f "x/user/model/sqlc.yaml"
- task: deploy:vault
- task: deploy:front
build:
desc: Build
watch: true
desc: Build all
cmds:
- task: build:vault
- task: build:front
start:vault:
desc: Start the vault
dir: "{{.ROOT_DIR}}/cmd/vault"
cmd: bun run start
start:front:
desc: Start the frontend
dir: "{{.ROOT_DIR}}/cmd/front"
cmd: bun run start
build:vault:
desc: Build the vault
dir: "{{.ROOT_DIR}}/cmd/vault"
cmd: bun run build
sources:
- "**/*.go"
- main.go
generates:
- web/app.wasm
deps:
- task: gen:templ
- build/app.wasm
build:front:
desc: Build the frontend
dir: "{{.ROOT_DIR}}/cmd/front"
cmd: bun run build
sources:
- main.go
generates:
- build/app.wasm
deploy:vault:
desc: Deploy the vault
dir: "{{.ROOT_DIR}}/cmd/vault"
cmds:
- bun run deploy
deploy:front:
desc: Deploy the frontend
dir: "{{.ROOT_DIR}}/cmd/front"
cmd: bun run deploy
gen:templ:
desc: Generate templ
cmds:
- templ generate
- rm -rf .task
gen:sqlc:
desc: Generate sqlc
cmd: sqlc generate
db:migrate:
desc: Migrate the database
prompt:
- Are you sure you want to run this command? This will delete all data in the database.
cmds:
- task: migrate:d1
- task: gen:sqlc
vars:
TIME:
sh: date
cmds:
- rm -rf web/vault.wasm
- GOOS=js GOARCH=wasm go build -o web/vault.wasm .
- gum log --time rfc822 --level info "[BUILD] WASM Complete"
serve:
desc: Serve
dir: web
cmds:
- bunx live-server
migrate:d1:
internal: true
desc: Migrate the common database
dir: "{{.ROOT_DIR}}/cmd/vault"
cmd: npm run migrate
sources:
- schema.sql
generates:
- schema.sql
test:
desc: Test
tidy:vault:
desc: Go mod tidy the vault
internal: true
dir: "{{.ROOT_DIR}}/cmd/vault"
cmds:
- go test -v ./...
- bun run clean
- rm -rf ./build
- rm -rf ./dist
tidy:front:
desc: Go mod tidy the frontend
internal: true
dir: "{{.ROOT_DIR}}/cmd/front"
cmds:
- bun run clean
- rm -rf ./build
- rm -rf ./dist
tidy:root:
desc: Go mod tidy the root
internal: true
dir: "{{.ROOT_DIR}}"
cmds:
- go mod tidy
- rm -rf .task

View File

@@ -1,50 +0,0 @@
//go:build js && wasm
// +build js,wasm
package app
import (
"encoding/json"
"github.com/labstack/echo/v4"
)
// New returns a new App instance
func New(opts ...Option) (*App, error) {
o := baseOptions()
for _, opt := range opts {
opt(o)
}
e := o.applyDefaults()
//
// identity.RegisterRoutes(e, o.conn.Identity)
// portfolio.RegisterRoutes(e, o.conn.Portfolio)
// user.RegisterRoutes(e, o.conn.User)
return &App{
Config: o.cfg,
Database: o.conn,
Echo: e,
}, nil
}
// handleIndex returns a simple JSON response
func handleIndex(c echo.Context) error {
params := make(map[string]string)
if err := json.NewDecoder(c.Request().Body).Decode(&params); err != nil {
return c.JSON(400, map[string]string{"message": err.Error()})
}
c.Response().Header().Set("Content-Type", "application/json")
return c.JSON(200, map[string]string{"message": "Hello, World!"})
}
// handleError returns a simple JSON response
func handleError() echo.HTTPErrorHandler {
return func(err error, c echo.Context) {
if he, ok := err.(*echo.HTTPError); ok {
// Log the error if needed
c.Logger().Errorf("Error: %v", he.Message)
}
}
}

View File

@@ -1,65 +0,0 @@
//go:build js && wasm
// +build js,wasm
package app
import (
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"github.com/onsonr/motr/pkg/config"
)
type App struct {
Config *config.MotrConfig
Database *config.DBConnection
Echo *echo.Echo
}
type Options struct {
conn *config.DBConnection
cfg *config.MotrConfig
mdws []echo.MiddlewareFunc
}
func (o *Options) applyDefaults() *echo.Echo {
e := echo.New()
e.IPExtractor = echo.ExtractIPDirect()
e.HTTPErrorHandler = handleError()
for _, mdw := range o.mdws {
e.Use(mdw)
}
e.POST("/", handleIndex)
return e
}
func baseOptions() *Options {
return &Options{
mdws: []echo.MiddlewareFunc{
echomiddleware.Logger(),
echomiddleware.Recover(),
},
}
}
type Option func(*Options)
// WithConfig sets the configuration for the application
func WithConfig(cfg *config.MotrConfig) func(*Options) {
return func(o *Options) {
o.cfg = cfg
}
}
// WithDatabase sets the database connection for the application
func WithDatabase(conn *config.DBConnection) func(*Options) {
return func(o *Options) {
o.conn = conn
}
}
// WithMiddleware sets the middleware for the application
func WithMiddleware(mdws ...echo.MiddlewareFunc) func(*Options) {
return func(o *Options) {
o.mdws = append(o.mdws, mdws...)
}
}

5
cmd/front/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
build
node_modules
.wrangler
.dev.vars*

27
cmd/front/main.go Normal file
View File

@@ -0,0 +1,27 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"github.com/sonr-io/motr/handlers/auth"
"github.com/sonr-io/motr/handlers/landing"
"github.com/sonr-io/motr/internal/config"
"github.com/sonr-io/motr/internal/middleware"
)
func main() {
// Setup config
e, c := config.New()
e.Use(middleware.UseSession(c), middleware.UseCloudflareCache(c))
// Register controllers
if err := landing.Register(c, e); err != nil {
panic(err)
}
if err := auth.Register(c, e); err != nil {
panic(err)
}
// Start server
e.Serve()
}

1540
cmd/front/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
cmd/front/package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "@sonr-io/motr-front",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "npm run clean && npm run assets && npm run wasm",
"assets": "go run github.com/syumai/workers/cmd/workers-assets-gen -mode=go",
"wasm": "GOOS=js GOARCH=wasm go build -o ./build/app.wasm .",
"deploy": "wrangler deploy",
"start": "wrangler dev",
"clean": "rm -rf ./build && rm -rf .wrangler && rm -rf ./dist && rm -rf ./node_modules && npm install"
},
"devDependencies": {
"wrangler": "^4.10.0"
}
}

47
cmd/front/wrangler.toml Normal file
View File

@@ -0,0 +1,47 @@
# Top-level configuration
name = "motr-front"
main = "build/worker.mjs"
compatibility_date = "2025-04-14"
routes = [
{ pattern = "sonr.id", custom_domain = true },
]
[build]
command = "npm run build"
[dev]
port = 8787
[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.id'
MATRIX_SERVER = 'https://bm.chat'
MOTR_GATEWAY = 'https://sonr.id'
MOTR_VAULT = 'https://did.run'
MOTR_MODE = 'resolver'
[observability]
enabled = true
logpush = true
[[r2_buckets]]
binding = 'PROFILES'
bucket_name = 'profiles'
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "motr-db"
database_id = "abc70ab3-32ce-4600-9b15-a452f92b7987"
[[kv_namespaces]]
binding = "SESSIONS" # available in your Worker on env.KV
id = "ea5de66fcfc14b5eba170395e29432ee"
[[kv_namespaces]]
binding = "HANDLES" # available in your Worker on env.KV
id = "271d47087a8842b2aac5ee79cf7bb203"

5
cmd/vault/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
build
node_modules
.wrangler
.dev.vars*

24
cmd/vault/main.go Normal file
View File

@@ -0,0 +1,24 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"github.com/sonr-io/motr/handlers/auth"
"github.com/sonr-io/motr/internal/config"
"github.com/sonr-io/motr/internal/middleware"
)
func main() {
// Setup config
e, c := config.New()
e.Use(middleware.UseSession(c), middleware.UseCloudflareCache(c))
// Register controllers
if err := auth.Register(c, e); err != nil {
panic(err)
}
// Start server
e.Serve()
}

6433
cmd/vault/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
cmd/vault/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "@sonr-io/motr-vault",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "npm run clean && npm run assets && npm run wasm",
"assets": "go run github.com/syumai/workers/cmd/workers-assets-gen -mode=go",
"wasm": "GOOS=js GOARCH=wasm go build -o ./build/app.wasm .",
"migrate": "npx wrangler d1 execute motr-db --remote --file=./schema.sql",
"deploy": "npx wrangler deploy",
"start": "npx wrangler dev",
"clean": "rm -rf ./build && rm -rf .wrangler && rm -rf ./dist && rm -rf ./node_modules && npm install"
},
"dependencies": {
"@extism/extism": "^2.0.0-rc11",
"@helia/dag-cbor": "^1.0.1",
"@helia/dag-json": "^1.0.1",
"@helia/json": "^1.0.1",
"@helia/strings": "^1.0.1",
"@helia/unixfs": "^1.4.1",
"helia": "^2.1.0",
"sonr-cosmes": "^0.0.5"
},
"devDependencies": {
"wrangler": "^4.10.0"
}
}

1026
cmd/vault/query.sql Normal file

File diff suppressed because it is too large Load Diff

356
cmd/vault/schema.sql Normal file
View File

@@ -0,0 +1,356 @@
-- 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)
);
-- 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
);
-- 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)
);
-- 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)
);
-- 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)
);
-- 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_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);
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_global_market_last_updated ON global_market(last_updated);
CREATE INDEX idx_global_market_deleted_at ON global_market(deleted_at);
CREATE INDEX idx_fear_greed_index_timestamp ON fear_greed_index(timestamp);
CREATE INDEX idx_fear_greed_index_value ON fear_greed_index(value);
CREATE INDEX idx_fear_greed_index_deleted_at ON fear_greed_index(deleted_at);
CREATE INDEX idx_crypto_listings_api_id ON crypto_listings(api_id);
CREATE INDEX idx_crypto_listings_symbol ON crypto_listings(symbol);
CREATE INDEX idx_crypto_listings_website_slug ON crypto_listings(website_slug);
CREATE INDEX idx_crypto_listings_deleted_at ON crypto_listings(deleted_at);
CREATE INDEX idx_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_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);

686
cmd/vault/worker.mjs Normal file
View File

@@ -0,0 +1,686 @@
export { default } from "./build/worker.mjs";
import { DurableObject } from "cloudflare:workers";
import createPlugin from "@extism/extism";
import { createHelia } from "helia";
import { strings } from "@helia/strings";
import { json } from "@helia/json";
import { dagJson } from "@helia/dag-json";
import { dagCbor } from "@helia/dag-cbor";
import { unixfs } from "@helia/unixfs";
import { serialiseSignDoc } from "sonr-cosmes/codec";
import { broadcastTx, RpcClient } from "sonr-cosmes/client";
import {
DidV1MsgRegisterController,
DwnV1QuerySpawnRequest,
} from "sonr-cosmes/protobufs";
export class Vault extends DurableObject {
constructor(ctx, env) {
super(ctx, env);
this.state = ctx;
this.env = env;
// Initialize node state
this.nodeState = {
status: "offline",
nodeId: null,
discoveredPeers: new Map(),
connectedPeers: new Map(),
logs: [],
};
// Initialize Helia node and related components when the DO is created
this.initializeHelia();
this.initializePlugins();
}
// Helper method to add a log entry
addLog(message) {
const timestamp = new Date().toISOString();
const logEntry = { timestamp, message };
this.nodeState.logs.push(logEntry);
// Keep only the last 100 log entries
if (this.nodeState.logs.length > 100) {
this.nodeState.logs.shift();
}
return logEntry;
}
async initializeHelia() {
try {
this.addLog("Initializing Helia node...");
// Create a Helia node with memory blockstore and datastore
this.helia = await createHelia({
blockstore: { type: "memory" },
datastore: { type: "memory" },
// Configure libp2p for Cloudflare Workers environment
libp2p: {
start: true,
addresses: { listen: [] },
connectionManager: {
minConnections: 0,
},
},
});
// Initialize various data handlers
this.stringHandler = strings(this.helia);
this.jsonHandler = json(this.helia);
this.dagJsonHandler = dagJson(this.helia);
this.dagCborHandler = dagCbor(this.helia);
this.fsHandler = unixfs(this.helia);
// Update node state
this.nodeState.status = this.helia.libp2p.status;
this.nodeState.nodeId = this.helia.libp2p.peerId.toString();
// Set up event listeners
this.helia.libp2p.addEventListener("peer:discovery", (evt) => {
const peerId = evt.detail.id.toString();
this.nodeState.discoveredPeers.set(peerId, {
id: peerId,
multiaddrs: evt.detail.multiaddrs
? evt.detail.multiaddrs.map((ma) => ma.toString())
: [],
discoveredAt: new Date().toISOString(),
});
this.addLog(`Discovered peer ${peerId}`);
});
this.helia.libp2p.addEventListener("peer:connect", (evt) => {
const peerId = evt.detail.toString();
this.nodeState.connectedPeers.set(peerId, {
id: peerId,
connectedAt: new Date().toISOString(),
});
this.addLog(`Connected to ${peerId}`);
});
this.helia.libp2p.addEventListener("peer:disconnect", (evt) => {
const peerId = evt.detail.toString();
this.nodeState.connectedPeers.delete(peerId);
this.addLog(`Disconnected from ${peerId}`);
});
this.addLog("Helia node initialized successfully");
return true;
} catch (error) {
this.addLog(`Failed to initialize Helia node: ${error.message}`);
console.error("Failed to initialize Helia node:", error);
return false;
}
}
// initializePlugins initializes the signer plugin
async initializePlugins() {
try {
// 1. Initialize the enclave plugin
this.addLog("Initializing enclave plugin...");
this.enclavePlugin = await createPlugin(
"https://cdn.sonr.io/bin/enclave.wasm",
{
useWasi: true,
},
);
this.addLog("Enclave plugin initialized successfully");
// 2. Initialize the signer plugin
this.addLog("Initializing signer plugin...");
this.signerPlugin = await createPlugin(
"https://cdn.sonr.io/bin/signer.wasm",
{
useWasi: true,
},
);
this.addLog("Signer plugin initialized successfully");
// 3. Initialize the verifier plugin
this.addLog("Initializing verifier plugin...");
this.verifierPlugin = await createPlugin(
"https://cdn.sonr.io/bin/verifier.wasm",
{
useWasi: true,
},
);
this.addLog("Verifier plugin initialized successfully");
return true;
} catch (error) {
this.addLog(`Failed to initialize plugin: ${error.message}`);
console.error("Failed to initialize plugin:", error);
return false;
}
}
// RPC method to get node status
async getNodeStatus() {
if (!this.helia) {
await this.initializeHelia();
}
return {
status: this.nodeState.status,
nodeId: this.nodeState.nodeId,
discoveredPeersCount: this.nodeState.discoveredPeers.size,
connectedPeersCount: this.nodeState.connectedPeers.size,
};
}
// RPC method to get node ID
async getNodeId() {
if (!this.helia) {
await this.initializeHelia();
}
return {
nodeId: this.nodeState.nodeId,
};
}
// RPC method to get discovered peers
async getDiscoveredPeers() {
if (!this.helia) {
await this.initializeHelia();
}
return {
count: this.nodeState.discoveredPeers.size,
peers: Array.from(this.nodeState.discoveredPeers.values()),
};
}
// RPC method to get connected peers
async getConnectedPeers() {
if (!this.helia) {
await this.initializeHelia();
}
return {
count: this.nodeState.connectedPeers.size,
peers: Array.from(this.nodeState.connectedPeers.values()),
};
}
// RPC method to get logs
async getLogs(limit = 50) {
if (!this.helia) {
await this.initializeHelia();
}
const logs = [...this.nodeState.logs];
return {
count: logs.length,
logs: logs.slice(-limit), // Return the most recent logs up to the limit
};
}
// RPC method to sign data
async sign(data) {
if (!this.plugin) {
await this.initializePlugin();
}
try {
let out = await this.plugin.call("sign", JSON.stringify(data));
return JSON.parse(out.text());
} catch (error) {
throw new Error(`Signing failed: ${error.message}`);
}
}
// Creates and signs a transaction
async createAndSignTx(msg, signer, options = {}) {
this.addLog(`Creating transaction with message type: ${msg.typeUrl}`);
try {
// Default options
const defaultOptions = {
memo: "",
fee: {
amount: [{ denom: "usnr", amount: "1000" }],
gas_limit: "200000",
},
chainId: this.env.SONR_CHAIN_ID || "sonr-testnet-1",
};
const txOptions = { ...defaultOptions, ...options };
// Create the sign doc
const signDoc = {
chainId: txOptions.chainId,
accountNumber: options.accountNumber || "0",
sequence: options.sequence || "0",
fee: txOptions.fee,
msgs: [msg],
memo: txOptions.memo,
};
// Serialize the sign doc
const signBytes = serialiseSignDoc(signDoc);
// Sign the transaction
this.addLog(`Signing transaction for ${signer}`);
const signature = await this.sign({
bytes: Buffer.from(signBytes).toString("base64"),
publicKey: options.publicKey,
});
// Create the signed transaction
const signedTx = {
signDoc,
signature: {
signature: signature.signature,
pub_key: {
type: "tendermint/PubKeySecp256k1",
value: signature.publicKey,
},
},
};
this.addLog("Transaction created and signed successfully");
return signedTx;
} catch (error) {
this.addLog(`Failed to create and sign transaction: ${error.message}`);
throw new Error(`Transaction creation failed: ${error.message}`);
}
}
// Broadcasts a signed transaction to the network
async broadcastTransaction(signedTx, broadcastMode = "BROADCAST_MODE_SYNC") {
this.addLog("Broadcasting transaction to network");
try {
const rpcUrl = this.env.SONR_RPC_URL || "https://rpc.sonr.io";
this.addLog(`Using RPC URL: ${rpcUrl}`);
const response = await broadcastTx(rpcUrl, signedTx, broadcastMode);
if (response.tx_response && response.tx_response.code === 0) {
this.addLog(
`Transaction broadcast successful. Hash: ${response.tx_response.txhash}`,
);
} else {
this.addLog(
`Transaction broadcast failed: ${JSON.stringify(response.tx_response)}`,
);
}
return response;
} catch (error) {
this.addLog(`Failed to broadcast transaction: ${error.message}`);
throw new Error(`Transaction broadcast failed: ${error.message}`);
}
}
// RPC method to add string content to IPFS
async addString(content) {
if (!this.helia) {
await this.initializeHelia();
}
if (!this.helia) {
throw new Error("Helia node not available");
}
try {
const cid = await this.stringHandler.add(content);
this.addLog(`Added string content with CID: ${cid.toString()}`);
return { cid: cid.toString() };
} catch (error) {
this.addLog(`Failed to add string: ${error.message}`);
throw new Error(`Failed to add string: ${error.message}`);
}
}
// RPC method to add JSON content to IPFS
async addJson(content) {
if (!this.helia) {
await this.initializeHelia();
}
if (!this.helia) {
throw new Error("Helia node not available");
}
try {
const cid = await this.jsonHandler.add(content);
this.addLog(`Added JSON content with CID: ${cid.toString()}`);
return { cid: cid.toString() };
} catch (error) {
this.addLog(`Failed to add JSON: ${error.message}`);
throw new Error(`Failed to add JSON: ${error.message}`);
}
}
// RPC method to get content from IPFS by CID
async getContent(cid) {
if (!this.helia) {
await this.initializeHelia();
}
if (!this.helia) {
throw new Error("Helia node not available");
}
try {
this.addLog(`Retrieving content for CID: ${cid}`);
// Try to get as JSON first
try {
const jsonData = await this.jsonHandler.get(cid);
return { type: "json", content: jsonData };
} catch (e) {
// Fall back to string if JSON fails
try {
const stringData = await this.stringHandler.get(cid);
return { type: "string", content: stringData };
} catch (e2) {
this.addLog(`Failed to retrieve content for CID: ${cid}`);
throw new Error("Failed to retrieve content");
}
}
} catch (error) {
this.addLog(`Error getting content: ${error.message}`);
throw new Error(`Failed to get content: ${error.message}`);
}
}
// RPC method to add DAG-JSON content to IPFS
async addDagJson(content) {
if (!this.helia) {
await this.initializeHelia();
}
if (!this.helia) {
throw new Error("Helia node not available");
}
try {
const cid = await this.dagJsonHandler.add(content);
this.addLog(`Added DAG-JSON content with CID: ${cid.toString()}`);
return { cid: cid.toString() };
} catch (error) {
this.addLog(`Failed to add DAG-JSON: ${error.message}`);
throw new Error(`Failed to add DAG-JSON: ${error.message}`);
}
}
// RPC method to add DAG-CBOR content to IPFS
async addDagCbor(content) {
if (!this.helia) {
await this.initializeHelia();
}
if (!this.helia) {
throw new Error("Helia node not available");
}
try {
const cid = await this.dagCborHandler.add(content);
this.addLog(`Added DAG-CBOR content with CID: ${cid.toString()}`);
return { cid: cid.toString() };
} catch (error) {
this.addLog(`Failed to add DAG-CBOR: ${error.message}`);
throw new Error(`Failed to add DAG-CBOR: ${error.message}`);
}
}
async registerDidController(did, controller, signer, options = {}) {
this.addLog(`Registering DID controller: ${controller} for DID: ${did}`);
try {
// Create the message
const msg = DidV1MsgRegisterController.create({
did: did,
controller: controller,
});
// Create and sign transaction
const signedTx = await this.createAndSignTx(msg, signer, options);
// Broadcast transaction
const response = await this.broadcastTransaction(signedTx);
this.addLog(
`DID controller registration response: ${JSON.stringify(response)}`,
);
return response;
} catch (error) {
this.addLog(`Failed to register DID controller: ${error.message}`);
throw new Error(`DID controller registration failed: ${error.message}`);
}
}
async spawnDwnVault(address, redirect, options = {}) {
this.addLog(
`Spawning DWN vault for address: ${address}, redirect: ${redirect}`,
);
try {
// Use RPC client to make the query
const rpcUrl =
options.rpcUrl || this.env.SONR_RPC_URL || "https://rpc.sonr.io";
this.addLog(`Using RPC URL: ${rpcUrl}`);
return new Promise((resolve, reject) => {
RpcClient.newBatchQuery(rpcUrl)
.add(
DwnV1QuerySpawnRequest,
{
cid: address,
redirect: redirect,
},
(err, res) => {
if (err) {
this.addLog(`Spawn DWN vault failed: ${err.message}`);
reject(err);
} else {
this.addLog(`Spawn DWN vault response: ${JSON.stringify(res)}`);
resolve(res);
}
},
)
.send();
});
} catch (error) {
this.addLog(`Failed to spawn DWN vault: ${error.message}`);
throw new Error(`DWN vault spawn failed: ${error.message}`);
}
}
// Retrieves account information needed for transaction signing
async getAccountInfo(address) {
this.addLog(`Getting account info for address: ${address}`);
try {
const rpcUrl = this.env.SONR_RPC_URL || "https://rpc.sonr.io";
const client = new RpcClient(rpcUrl);
const accountInfo = await client.getAccount(address);
this.addLog(`Account info retrieved successfully`);
return {
accountNumber: accountInfo.account_number,
sequence: accountInfo.sequence,
};
} catch (error) {
this.addLog(`Failed to get account info: ${error.message}`);
throw new Error(`Account info retrieval failed: ${error.message}`);
}
}
// Fetch handler for backward compatibility and Go WASM server interaction
async fetch(request) {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method;
try {
// Handle different API endpoints based on the path
// Helia node status endpoints
if (path === "/helia/status") {
return new Response(JSON.stringify(await this.getNodeStatus()), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/helia/node-id") {
return new Response(JSON.stringify(await this.getNodeId()), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/helia/discovered-peers") {
return new Response(JSON.stringify(await this.getDiscoveredPeers()), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/helia/connected-peers") {
return new Response(JSON.stringify(await this.getConnectedPeers()), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/helia/logs") {
const limitParam = url.searchParams.get("limit");
const limit = limitParam ? parseInt(limitParam, 10) : 50;
return new Response(JSON.stringify(await this.getLogs(limit)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
// IPFS content endpoints
if (path === "/ipfs/add/string" && method === "POST") {
const content = await request.text();
return new Response(JSON.stringify(await this.addString(content)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/ipfs/add/json" && method === "POST") {
const content = await request.json();
return new Response(JSON.stringify(await this.addJson(content)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path.startsWith("/ipfs/get/") && method === "GET") {
const cid = path.replace("/ipfs/get/", "");
const result = await this.getContent(cid);
if (result.type === "json") {
return new Response(JSON.stringify(result.content), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} else {
return new Response(result.content, {
status: 200,
headers: { "Content-Type": "text/plain" },
});
}
}
if (path === "/ipfs/dag/json" && method === "POST") {
const content = await request.json();
return new Response(JSON.stringify(await this.addDagJson(content)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (path === "/ipfs/dag/cbor" && method === "POST") {
const content = await request.json();
return new Response(JSON.stringify(await this.addDagCbor(content)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
// Signing endpoint
if (path === "/vault/sign" && method === "POST") {
const data = await request.json();
return new Response(JSON.stringify(await this.sign(data)), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
// DID controller registration endpoint
if (path === "/did/register-controller" && method === "POST") {
const data = await request.json();
const { did, controller, signer, options } = data;
return new Response(
JSON.stringify(
await this.registerDidController(did, controller, signer, options),
),
{
status: 200,
headers: { "Content-Type": "application/json" },
},
);
}
// DWN vault spawning endpoint
if (path === "/dwn/spawn" && method === "POST") {
const data = await request.json();
const { address, redirect, options } = data;
return new Response(
JSON.stringify(await this.spawnDwnVault(address, redirect, options)),
{
status: 200,
headers: { "Content-Type": "application/json" },
},
);
}
// Account info endpoint
if (path === "/account/info" && method === "GET") {
const address = url.searchParams.get("address");
if (!address) {
return new Response(
JSON.stringify({ error: "Address parameter is required" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
},
);
}
return new Response(
JSON.stringify(await this.getAccountInfo(address)),
{
status: 200,
headers: { "Content-Type": "application/json" },
},
);
}
// Default response for unhandled paths
return new Response(JSON.stringify({ error: "Not found" }), {
status: 404,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
// Log the error
this.addLog(`Error handling request to ${path}: ${error.message}`);
// Return error response
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
}
}

53
cmd/vault/wrangler.toml Normal file
View File

@@ -0,0 +1,53 @@
# Top-level configuration
name = "motr-vault"
main = "./worker.mjs"
compatibility_date = "2025-04-14"
routes = [
{ pattern = "did.run", custom_domain = true },
]
[build]
command = "npm run build"
[dev]
port = 6969
[observability]
enabled = true
logpush = true
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "motr-controller-db"
database_id = "872a4b08-7e07-4978-b227-5b60940238ed"
[[kv_namespaces]]
binding = "SESSIONS" # available in your Worker on env.KV
id = "ea5de66fcfc14b5eba170395e29432ee"
[[kv_namespaces]]
binding = "HANDLES" # available in your Worker on env.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'
MOTR_MODE = 'controller'
[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: `components/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: `components/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: `components/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: `components/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: `components/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: `components/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: `components/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: `components/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

View File

@@ -0,0 +1,83 @@
package dashboard
// StatusCard component for displaying Helia node status
templ HeliaStatusCard() {
<div class="p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="flex justify-between items-center mb-1">
<h2 class="text-4xl font-semibold text-gray-800 dark:text-neutral-200">
<span id="statusValue">Offline</span>
</h2>
<div class="flex items-center -space-x-2">
<img class="shrink-0 size-7 rounded-full" src="https://images.unsplash.com/photo-1659482633369-9fe69af50bfb?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=3&w=320&h=320&q=80" alt="Avatar"/>
</div>
</div>
<h3 class="text-gray-500 dark:text-neutral-500">
Status
</h3>
</div>
}
// NodeIDCard component for displaying Helia node ID
templ HeliaNodeIDCard() {
<div>
<div class="p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="flex justify-between items-center mb-1">
<h2 class="text-4xl font-semibold text-gray-800 dark:text-neutral-200">
Node ID
</h2>
</div>
</div>
<div class="inline-flex items-center gap-x-2">
<h3 class="text-gray-500 dark:text-neutral-500">
<span id="nodeId">-</span>
</h3>
</div>
</div>
}
// DiscoveredPeersCard component for displaying discovered peers count
templ HeliaDiscoveredPeersCard() {
<div class="p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="flex justify-between items-center mb-1">
<h2 class="text-4xl font-semibold text-gray-800 dark:text-neutral-200">
<span id="discoveredPeerCount">0</span>
</h2>
</div>
<div class="inline-flex items-center gap-x-2">
<h3 class="text-gray-500 dark:text-neutral-500">
Discovered Peers
</h3>
</div>
</div>
}
// ConnectedPeersCard component for displaying connected peers count
templ HeliaConnectedPeersCard() {
<div class="p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="flex justify-between items-center mb-1">
<h2 class="text-4xl font-semibold text-gray-800 dark:text-neutral-200">
<span id="connectedPeerCount">0</span>
</h2>
<div class="flex items-center -space-x-2"></div>
</div>
<h3 class="text-gray-500 dark:text-neutral-500">
Connected Peers
</h3>
</div>
}
// Optional: Create a component for a connected peers list if needed
templ HeliaConnectedPeersList() {
<div class="p-4 bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<h3 class="text-lg font-medium text-gray-800 dark:text-neutral-200 mb-2">Connected Peers List</h3>
<ul id="connectedPeersList" class="text-sm text-gray-600 dark:text-neutral-400"></ul>
</div>
}
// Optional: Create a component for the running log
templ HeliaRunningLog() {
<div class="p-4 bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<h3 class="text-lg font-medium text-gray-800 dark:text-neutral-200 mb-2">Running Log</h3>
<div id="runningLog" class="text-sm text-gray-600 dark:text-neutral-400 h-40 overflow-y-auto font-mono"></div>
</div>
}

View File

@@ -0,0 +1,191 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package dashboard
//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"
// StatusCard component for displaying Helia node status
func HeliaStatusCard() 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, "<div class=\"p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><div class=\"flex justify-between items-center mb-1\"><h2 class=\"text-4xl font-semibold text-gray-800 dark:text-neutral-200\"><span id=\"statusValue\">Offline</span></h2><div class=\"flex items-center -space-x-2\"><img class=\"shrink-0 size-7 rounded-full\" src=\"https://images.unsplash.com/photo-1659482633369-9fe69af50bfb?ixlib=rb-4.0.3&amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;auto=format&amp;fit=facearea&amp;facepad=3&amp;w=320&amp;h=320&amp;q=80\" alt=\"Avatar\"></div></div><h3 class=\"text-gray-500 dark:text-neutral-500\">Status</h3></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// NodeIDCard component for displaying Helia node ID
func HeliaNodeIDCard() 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, "<div><div class=\"p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><div class=\"flex justify-between items-center mb-1\"><h2 class=\"text-4xl font-semibold text-gray-800 dark:text-neutral-200\">Node ID</h2></div></div><div class=\"inline-flex items-center gap-x-2\"><h3 class=\"text-gray-500 dark:text-neutral-500\"><span id=\"nodeId\">-</span></h3></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// DiscoveredPeersCard component for displaying discovered peers count
func HeliaDiscoveredPeersCard() 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, 3, "<div class=\"p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><div class=\"flex justify-between items-center mb-1\"><h2 class=\"text-4xl font-semibold text-gray-800 dark:text-neutral-200\"><span id=\"discoveredPeerCount\">0</span></h2></div><div class=\"inline-flex items-center gap-x-2\"><h3 class=\"text-gray-500 dark:text-neutral-500\">Discovered Peers</h3></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// ConnectedPeersCard component for displaying connected peers count
func HeliaConnectedPeersCard() 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, 4, "<div class=\"p-4 flex flex-col bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><div class=\"flex justify-between items-center mb-1\"><h2 class=\"text-4xl font-semibold text-gray-800 dark:text-neutral-200\"><span id=\"connectedPeerCount\">0</span></h2><div class=\"flex items-center -space-x-2\"></div></div><h3 class=\"text-gray-500 dark:text-neutral-500\">Connected Peers</h3></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Optional: Create a component for a connected peers list if needed
func HeliaConnectedPeersList() 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, 5, "<div class=\"p-4 bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><h3 class=\"text-lg font-medium text-gray-800 dark:text-neutral-200 mb-2\">Connected Peers List</h3><ul id=\"connectedPeersList\" class=\"text-sm text-gray-600 dark:text-neutral-400\"></ul></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Optional: Create a component for the running log
func HeliaRunningLog() 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_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"p-4 bg-white border border-gray-200 rounded-xl dark:bg-neutral-800 dark:border-neutral-700\"><h3 class=\"text-lg font-medium text-gray-800 dark:text-neutral-200 mb-2\">Running Log</h3><div id=\"runningLog\" class=\"text-sm text-gray-600 dark:text-neutral-400 h-40 overflow-y-auto font-mono\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,8 @@
package inputs
templ InputAvatar(value string, helpText string) {
<input type="file" name="avatar" id="avatar" name="avatar" accept="image/*"/>
<sl-button variant="neutral" slot="suffix" type="button">
<sl-icon name="upload-2-fill" slot="prefix"></sl-icon>
</sl-button>
}

View File

@@ -1,14 +1,14 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package view
package inputs
//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 allocationView(data any) templ.Component {
func InputAvatar(value string, helpText 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 {
@@ -29,7 +29,7 @@ func allocationView(data any) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div><h1>Allocation</h1></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<input type=\"file\" name=\"avatar\" id=\"avatar\" name=\"avatar\" accept=\"image/*\"> <sl-button variant=\"neutral\" slot=\"suffix\" type=\"button\"><sl-icon name=\"upload-2-fill\" slot=\"prefix\"></sl-icon></sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -0,0 +1,9 @@
package inputs
templ InputBio(value string, helpText string) {
<sl-textarea name="bio" placeholder="Bio" type="text" label="Bio" minlength="4" maxlength="12" required class="border-red-500" value={ value } help-text={ helpText }>
<div slot="prefix">
<sl-icon name="at-sign" library="sonr"></sl-icon>
</div>
</sl-textarea>
}

View File

@@ -0,0 +1,66 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package inputs
//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 InputBio(value string, helpText 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_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, "<sl-textarea name=\"bio\" placeholder=\"Bio\" type=\"text\" label=\"Bio\" minlength=\"4\" maxlength=\"12\" required class=\"border-red-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/inputs/input_bio.templ`, Line: 4, Col: 141}
}
_, 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, "\" help-text=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(helpText)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/inputs/input_bio.templ`, Line: 4, Col: 164}
}
_, 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, "\"><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-textarea>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,33 @@
package inputs
templ HandleInitial() {
<div hx-target="this" hx-swap="outerHTML">
<sl-input name="handle" placeholder="digitalgold" type="text" label="Handle" minlength="4" maxlength="12" required hx-post="/register/profile" hx-indicator="#handle-indicator" autofocus>
<div slot="prefix">
<sl-icon name="at-sign" library="sonr"></sl-icon>
</div>
</sl-input>
</div>
<br/>
}
templ HandleError(value string, helpText string) {
<sl-input name="handle" placeholder="digitalgold" type="text" label="Handle" minlength="4" maxlength="12" required class="border-red-500" value={ value } help-text={ helpText }>
<div slot="prefix">
<sl-icon name="at-sign" library="sonr"></sl-icon>
</div>
<div slot="suffix" style="color: #B54549;">
<sl-icon name="x"></sl-icon>
</div>
</sl-input>
<br/>
}
templ HandleSuccess(value string) {
<sl-input name="handle" placeholder="digitalgold" type="text" label="Handle" minlength="4" maxlength="12" required class="border-green-500" value={ value } disabled>
<div slot="prefix" style="color: #46A758;">
<sl-icon name="at-sign" library="sonr"></sl-icon>
</div>
</sl-input>
<br/>
}

View File

@@ -0,0 +1,137 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package inputs
//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 HandleInitial() 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, "<div hx-target=\"this\" hx-swap=\"outerHTML\"><sl-input name=\"handle\" placeholder=\"digitalgold\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required hx-post=\"/register/profile\" hx-indicator=\"#handle-indicator\" autofocus><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-input></div><br>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func HandleError(value string, helpText 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_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, "<sl-input name=\"handle\" placeholder=\"digitalgold\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required class=\"border-red-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/inputs/input_handle.templ`, Line: 15, Col: 152}
}
_, 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, "\" help-text=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(helpText)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/inputs/input_handle.templ`, Line: 15, Col: 175}
}
_, 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, "\"><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div><div slot=\"suffix\" style=\"color: #B54549;\"><sl-icon name=\"x\"></sl-icon></div></sl-input><br>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func HandleSuccess(value 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_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, 5, "<sl-input name=\"handle\" placeholder=\"digitalgold\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required class=\"border-green-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/inputs/input_handle.templ`, Line: 27, Col: 154}
}
_, 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, "\" disabled><div slot=\"prefix\" style=\"color: #46A758;\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-input><br>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,111 @@
package inputs
templ PasskeyInitial(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-neutral-500"></sl-icon>
Register Passkey
</sl-button>
}
templ PasskeyError(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-red-500"></sl-icon>
Register Passkey
</sl-button>
}
templ PasskeySuccess(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-green-500"></sl-icon>
Register Passkey
</sl-button>
}
script NavigatorCredentialsCreate(userId string, userHandle string, challenge string) {
const publicKey = {
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
rp: {
name: "Sonr.ID",
},
user: {
// Assuming that userId is ASCII-only
id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
name: userId,
displayName: userHandle,
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7, // "ES256"
},
{
type: "public-key",
alg: -257, // "RS256"
},
],
authenticatorSelection: {
userVerification: "required",
residentKey: "required",
authenticatorAttachment: "platform",
},
timeout: 60000, // 1 minute
extensions: {
payment: {
isPayment: true,
},
largeBlob: {
supported: "preferred",
},
},
};
// Helper function to convert ArrayBuffer to Base64URL string
function arrayBufferToBase64URL(buffer) {
const bytes = new Uint8Array(buffer);
let str = '';
bytes.forEach(byte => { str += String.fromCharCode(byte) });
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
navigator.credentials
.create({ publicKey })
.then((newCredentialInfo) => {
if (!(newCredentialInfo instanceof PublicKeyCredential)) {
throw new Error('Received credential is not a PublicKeyCredential');
}
const response = newCredentialInfo.response;
if (!(response instanceof AuthenticatorAttestationResponse)) {
throw new Error('Response is not an AuthenticatorAttestationResponse');
}
// Convert the credential data to a cross-platform compatible format
const credentialJSON = {
id: newCredentialInfo.id,
rawId: arrayBufferToBase64URL(newCredentialInfo.rawId),
type: newCredentialInfo.type,
authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null,
transports: Array.isArray(response.getTransports) ? response.getTransports() : [],
clientExtensionResults: newCredentialInfo.getClientExtensionResults(),
response: {
attestationObject: arrayBufferToBase64URL(response.attestationObject),
clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON)
}
};
// Set the form value with the stringified credential data
const credential = document.getElementById('credential-data');
credential.value = JSON.stringify(credentialJSON);
// Submit the form
const form = document.getElementById('passkey-form');
form.submit();
})
.catch((err) => {
console.error('Passkey creation failed:', err);
alert(`Failed to create passkey: ${err.message || 'Unknown error'}`);
});
}

View File

@@ -0,0 +1,232 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package inputs
//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 PasskeyInitial(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-neutral-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func PasskeyError(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-red-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func PasskeySuccess(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-green-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func NavigatorCredentialsCreate(userId string, userHandle string, challenge string) templ.ComponentScript {
return templ.ComponentScript{
Name: `__templ_NavigatorCredentialsCreate_8f96`,
Function: `function __templ_NavigatorCredentialsCreate_8f96(userId, userHandle, challenge){const publicKey = {
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
rp: {
name: "Sonr.ID",
},
user: {
// Assuming that userId is ASCII-only
id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
name: userId,
displayName: userHandle,
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7, // "ES256"
},
{
type: "public-key",
alg: -257, // "RS256"
},
],
authenticatorSelection: {
userVerification: "required",
residentKey: "required",
authenticatorAttachment: "platform",
},
timeout: 60000, // 1 minute
extensions: {
payment: {
isPayment: true,
},
largeBlob: {
supported: "preferred",
},
},
};
// Helper function to convert ArrayBuffer to Base64URL string
function arrayBufferToBase64URL(buffer) {
const bytes = new Uint8Array(buffer);
let str = '';
bytes.forEach(byte => { str += String.fromCharCode(byte) });
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
navigator.credentials
.create({ publicKey })
.then((newCredentialInfo) => {
if (!(newCredentialInfo instanceof PublicKeyCredential)) {
throw new Error('Received credential is not a PublicKeyCredential');
}
const response = newCredentialInfo.response;
if (!(response instanceof AuthenticatorAttestationResponse)) {
throw new Error('Response is not an AuthenticatorAttestationResponse');
}
// Convert the credential data to a cross-platform compatible format
const credentialJSON = {
id: newCredentialInfo.id,
rawId: arrayBufferToBase64URL(newCredentialInfo.rawId),
type: newCredentialInfo.type,
authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null,
transports: Array.isArray(response.getTransports) ? response.getTransports() : [],
clientExtensionResults: newCredentialInfo.getClientExtensionResults(),
response: {
attestationObject: arrayBufferToBase64URL(response.attestationObject),
clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON)
}
};
// Set the form value with the stringified credential data
const credential = document.getElementById('credential-data');
credential.value = JSON.stringify(credentialJSON);
// Submit the form
const form = document.getElementById('passkey-form');
form.submit();
})
.catch((err) => {
console.error('Passkey creation failed:', err);
alert(` + "`" + `Failed to create passkey: ${err.message || 'Unknown error'}` + "`" + `);
});
}`,
Call: templ.SafeScript(`__templ_NavigatorCredentialsCreate_8f96`, userId, userHandle, challenge),
CallInline: templ.SafeScriptInline(`__templ_NavigatorCredentialsCreate_8f96`, userId, userHandle, challenge),
}
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,37 @@
package wallet
templ AssetsDropdown() {
<sl-select
label="Accounts"
name="select-assets"
value="SNR BTC ETH"
help-text="Select Blockchains to connect with your Vault"
multiple
class="custom-tag py-2"
>
<sl-option value="SNR">
<sl-icon slot="prefix" name="SNR" library="crypto"></sl-icon>
Sonr
</sl-option>
<sl-option value="BTC">
<sl-icon slot="prefix" name="BTC" library="crypto"></sl-icon>
Bitcoin
</sl-option>
<sl-option value="ETH">
<sl-icon slot="prefix" name="ETH" library="crypto"></sl-icon>
Ethereum
</sl-option>
<sl-option value="SOL">
<sl-icon slot="prefix" name="SOL" library="crypto"></sl-icon>
Solana
</sl-option>
<sl-option value="LTC">
<sl-icon slot="prefix" name="LTC" library="crypto"></sl-icon>
Litecoin
</sl-option>
<sl-option value="DOGE">
<sl-icon slot="prefix" name="DOGE" library="crypto"></sl-icon>
Dogecoin
</sl-option>
</sl-select>
}

View File

@@ -0,0 +1,40 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package wallet
//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 AssetsDropdown() 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, "<sl-select label=\"Accounts\" name=\"select-assets\" value=\"SNR BTC ETH\" help-text=\"Select Blockchains to connect with your Vault\" multiple class=\"custom-tag py-2\"><sl-option value=\"SNR\"><sl-icon slot=\"prefix\" name=\"SNR\" library=\"crypto\"></sl-icon> Sonr</sl-option> <sl-option value=\"BTC\"><sl-icon slot=\"prefix\" name=\"BTC\" library=\"crypto\"></sl-icon> Bitcoin</sl-option> <sl-option value=\"ETH\"><sl-icon slot=\"prefix\" name=\"ETH\" library=\"crypto\"></sl-icon> Ethereum</sl-option> <sl-option value=\"SOL\"><sl-icon slot=\"prefix\" name=\"SOL\" library=\"crypto\"></sl-icon> Solana</sl-option> <sl-option value=\"LTC\"><sl-icon slot=\"prefix\" name=\"LTC\" library=\"crypto\"></sl-icon> Litecoin</sl-option> <sl-option value=\"DOGE\"><sl-icon slot=\"prefix\" name=\"DOGE\" library=\"crypto\"></sl-icon> Dogecoin</sl-option></sl-select>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,67 @@
package wallet
type CoinInfo struct {
Ticker string
Name string
IsDefault bool
}
var defaultCoins = []CoinInfo{
{Ticker: "SNR", Name: "Sonr", IsDefault: true},
{Ticker: "BTC", Name: "Bitcoin", IsDefault: true},
{Ticker: "ETH", Name: "Ethereum", IsDefault: true},
{Ticker: "SOL", Name: "Solana", IsDefault: false},
{Ticker: "LTC", Name: "Litecoin", IsDefault: false},
{Ticker: "DOGE", Name: "Dogecoin", IsDefault: false},
{Ticker: "XRP", Name: "Ripple", IsDefault: false},
{Ticker: "OSMO", Name: "Osmosis", IsDefault: false},
{Ticker: "ATOM", Name: "Cosmos", IsDefault: false},
{Ticker: "STARZ", Name: "Stargaze", IsDefault: false},
{Ticker: "AKT", Name: "Akash", IsDefault: false},
{Ticker: "EVMOS", Name: "Evmos", IsDefault: false},
{Ticker: "FIL", Name: "Filecoin", IsDefault: false},
{Ticker: "AXL", Name: "Axelar", IsDefault: false},
}
templ CoinsDropdown() {
<sl-select
label="Accounts"
name="selected_assets"
value="SNR BTC ETH"
help-text="Select Blockchains to connect with your Vault"
multiple
class="custom-tag py-2"
>
for _, a := range defaultCoins {
@CoinOption(a)
}
</sl-select>
<script>
const select = document.querySelector('.custom-tag');
select.getTag = (option, index) => {
const name = option.querySelector('sl-icon[slot="prefix"]').name;
return `
<sl-tag removable>
<sl-icon name="${name}" library="crypto" style="padding-inline-end: .5rem;"></sl-icon>
${option.getTextLabel()}
</sl-tag>
`;
};
</script>
}
templ CoinOption(a CoinInfo) {
if a.IsDefault {
<sl-option value={ a.Ticker } selected disabled>
<sl-icon slot="prefix" name={ a.Ticker } library="crypto"></sl-icon>
{ a.Name }
</sl-option>
<sl-divider></sl-divider>
} else {
<sl-option value={ a.Ticker }>
<sl-icon slot="prefix" name={ a.Ticker } library="crypto"></sl-icon>
{ a.Name }
</sl-option>
<sl-divider></sl-divider>
}
}

View File

@@ -0,0 +1,187 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package wallet
//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"
type CoinInfo struct {
Ticker string
Name string
IsDefault bool
}
var defaultCoins = []CoinInfo{
{Ticker: "SNR", Name: "Sonr", IsDefault: true},
{Ticker: "BTC", Name: "Bitcoin", IsDefault: true},
{Ticker: "ETH", Name: "Ethereum", IsDefault: true},
{Ticker: "SOL", Name: "Solana", IsDefault: false},
{Ticker: "LTC", Name: "Litecoin", IsDefault: false},
{Ticker: "DOGE", Name: "Dogecoin", IsDefault: false},
{Ticker: "XRP", Name: "Ripple", IsDefault: false},
{Ticker: "OSMO", Name: "Osmosis", IsDefault: false},
{Ticker: "ATOM", Name: "Cosmos", IsDefault: false},
{Ticker: "STARZ", Name: "Stargaze", IsDefault: false},
{Ticker: "AKT", Name: "Akash", IsDefault: false},
{Ticker: "EVMOS", Name: "Evmos", IsDefault: false},
{Ticker: "FIL", Name: "Filecoin", IsDefault: false},
{Ticker: "AXL", Name: "Axelar", IsDefault: false},
}
func CoinsDropdown() 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, "<sl-select label=\"Accounts\" name=\"selected_assets\" value=\"SNR BTC ETH\" help-text=\"Select Blockchains to connect with your Vault\" multiple class=\"custom-tag py-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, a := range defaultCoins {
templ_7745c5c3_Err = CoinOption(a).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</sl-select><script>\n\t const select = document.querySelector('.custom-tag');\n select.getTag = (option, index) => {\n const name = option.querySelector('sl-icon[slot=\"prefix\"]').name;\n return `\n <sl-tag removable>\n <sl-icon name=\"${name}\" library=\"crypto\" style=\"padding-inline-end: .5rem;\"></sl-icon>\n ${option.getTextLabel()}\n </sl-tag>\n `;\n };\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func CoinOption(a CoinInfo) 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)
if a.IsDefault {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<sl-option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(a.Ticker)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 55, Col: 29}
}
_, 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, 4, "\" selected disabled><sl-icon slot=\"prefix\" name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(a.Ticker)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 56, Col: 41}
}
_, 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, 5, "\" library=\"crypto\"></sl-icon> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(a.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 57, Col: 11}
}
_, 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, 6, "</sl-option> <sl-divider></sl-divider>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<sl-option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(a.Ticker)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 61, Col: 29}
}
_, 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, 8, "\"><sl-icon slot=\"prefix\" name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(a.Ticker)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 62, Col: 41}
}
_, 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, 9, "\" library=\"crypto\"></sl-icon> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(a.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/menus/coins_dropdown.templ`, Line: 63, Col: 11}
}
_, 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, 10, "</sl-option> <sl-divider></sl-divider>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,91 @@
package views
import (
"github.com/sonr-io/motr/internal/ui"
"github.com/sonr-io/motr/components/dashboard"
"time"
)
var heliaCardScriptHandle = templ.NewOnceHandle()
func DemoView(d time.Time) templ.Component {
return demoComponent(d)
}
templ demoComponent(d time.Time) {
@ui.HTML() {
@ui.Head() {
}
<sl-tab-group>
@ui.Nav() {
@ui.NavLeft() {
@ui.NavLogo("Motr")
}
@ui.NavRight() {
<sl-tab slot="nav" panel="tab-1">Time</sl-tab>
}
}
@ui.Body() {
<sl-tab-panel name="tab-1" active>
@ui.Container() {
<div class="space-y-6">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
@dashboard.HeliaStatusCard()
@dashboard.HeliaNodeIDCard()
@dashboard.HeliaDiscoveredPeersCard()
@dashboard.HeliaConnectedPeersCard()
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@dashboard.HeliaConnectedPeersList()
@dashboard.HeliaRunningLog()
</div>
@heliaCardScriptHandle.Once() {
<script>
document.addEventListener('DOMContentLoaded', async () => {
try {
// Use the globally available instantiateHeliaNode function
const helia = window.helia = await window.instantiateHeliaNode()
window.heliaFs = await HeliaUnixfs.unixfs(helia)
helia.libp2p.addEventListener('peer:discovery', (evt) => {
window.discoveredPeers.set(evt.detail.id.toString(), evt.detail)
addToLog(`Discovered peer ${evt.detail.id.toString()}`)
})
helia.libp2p.addEventListener('peer:connect', (evt) => {
addToLog(`Connected to ${evt.detail.toString()}`)
})
helia.libp2p.addEventListener('peer:disconnect', (evt) => {
addToLog(`Disconnected from ${evt.detail.toString()}`)
})
setInterval(() => {
const statusValueEl = document.getElementById('statusValue')
if (statusValueEl) {
statusValueEl.innerHTML = helia.libp2p.status === 'started' ? 'Online' : 'Offline'
}
updateConnectedPeers()
updateDiscoveredPeers()
}, 500)
const id = await helia.libp2p.peerId.toString()
const nodeIdEl = document.getElementById('nodeId')
if (nodeIdEl) {
nodeIdEl.innerHTML = id
}
addToLog('Helia node initialized successfully')
} catch (err) {
addToLog(`Error initializing Helia: ${err.message}`)
console.error('Error initializing Helia:', err)
}
})
</script>
}
</div>
}
</sl-tab-panel>
}
</sl-tab-group>
}
}

View File

@@ -0,0 +1,264 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package views
//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/sonr-io/motr/components/dashboard"
"github.com/sonr-io/motr/internal/ui"
"time"
)
var heliaCardScriptHandle = templ.NewOnceHandle()
func DemoView(d time.Time) templ.Component {
return demoComponent(d)
}
func demoComponent(d time.Time) 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_Var3 := 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)
return nil
})
templ_7745c5c3_Err = ui.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var5 := 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 = ui.NavLogo("Motr").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.NavLeft().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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, "<sl-tab slot=\"nav\" panel=\"tab-1\">Time</sl-tab>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.NavRight().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Nav().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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, 4, "<sl-tab-panel name=\"tab-1\" active>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var8 := 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, 5, "<div class=\"space-y-6\"><div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaStatusCard().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaNodeIDCard().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaDiscoveredPeersCard().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaConnectedPeersCard().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><div class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaConnectedPeersList().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dashboard.HeliaRunningLog().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var9 := 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, 8, "<script>\n document.addEventListener('DOMContentLoaded', async () => {\n try {\n // Use the globally available instantiateHeliaNode function\n const helia = window.helia = await window.instantiateHeliaNode()\n window.heliaFs = await HeliaUnixfs.unixfs(helia)\n\n helia.libp2p.addEventListener('peer:discovery', (evt) => {\n window.discoveredPeers.set(evt.detail.id.toString(), evt.detail)\n addToLog(`Discovered peer ${evt.detail.id.toString()}`)\n })\n\n helia.libp2p.addEventListener('peer:connect', (evt) => {\n addToLog(`Connected to ${evt.detail.toString()}`)\n })\n \n helia.libp2p.addEventListener('peer:disconnect', (evt) => {\n addToLog(`Disconnected from ${evt.detail.toString()}`)\n })\n\n setInterval(() => {\n const statusValueEl = document.getElementById('statusValue')\n if (statusValueEl) {\n statusValueEl.innerHTML = helia.libp2p.status === 'started' ? 'Online' : 'Offline'\n }\n updateConnectedPeers()\n updateDiscoveredPeers()\n }, 500)\n\n const id = await helia.libp2p.peerId.toString()\n const nodeIdEl = document.getElementById('nodeId')\n if (nodeIdEl) {\n nodeIdEl.innerHTML = id\n }\n addToLog('Helia node initialized successfully')\n } catch (err) {\n addToLog(`Error initializing Helia: ${err.message}`)\n console.error('Error initializing Helia:', err)\n }\n })\n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = heliaCardScriptHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</sl-tab-panel>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.HTML().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,48 @@
package views
import (
"github.com/sonr-io/motr/internal/sink"
"github.com/sonr-io/motr/internal/ui"
)
var meta = sink.DefaultMetadata()
func HomeView() templ.Component {
return homeComponent()
}
templ homeComponent() {
@ui.HTML() {
@ui.Head() {
@ui.MetaComponent(meta)
}
<sl-tab-group>
@ui.Nav() {
@ui.NavLeft() {
@ui.NavLogo("Motr")
}
@ui.NavRight() {
}
}
@ui.Body() {
@ui.Container() {
<sl-card>
<div slot="header">
<h3 class="card-title">Welcome to Motr</h3>
</div>
<div class="card-body">
<p>This is a demo of the Motr web app.</p>
<p>You can use this app to manage your Motr account.</p>
</div>
<div class="card-footer">
<sl-button type="primary" size="large" href="/register">
<sl-icon slot="prefix" library="sonr" name="passkey"></sl-icon>
Register
</sl-button>
</div>
</sl-card>
}
}
</sl-tab-group>
}
}

View File

@@ -0,0 +1,197 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package views
//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/sonr-io/motr/internal/sink"
"github.com/sonr-io/motr/internal/ui"
)
var meta = sink.DefaultMetadata()
func HomeView() templ.Component {
return homeComponent()
}
func homeComponent() 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_Var3 := 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 = ui.MetaComponent(meta).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var5 := 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 = ui.NavLogo("Motr").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.NavLeft().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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)
return nil
})
templ_7745c5c3_Err = ui.NavRight().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Nav().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var8 := 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, "<sl-card><div slot=\"header\"><h3 class=\"card-title\">Welcome to Motr</h3></div><div class=\"card-body\"><p>This is a demo of the Motr web app.</p><p>You can use this app to manage your Motr account.</p></div><div class=\"card-footer\"><sl-button type=\"primary\" size=\"large\" href=\"/register\"><sl-icon slot=\"prefix\" library=\"sonr\" name=\"passkey\"></sl-icon> Register</sl-button></div></sl-card>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.HTML().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,25 @@
package views
import (
"github.com/sonr-io/motr/internal/ui"
)
templ LoginView() {
@ui.HTML() {
@ui.Head() {
@ui.MetaComponent(meta)
}
<sl-tab-group>
@ui.Nav() {
@ui.NavLeft() {
@ui.NavLogo("Motr")
}
@ui.NavRight() {
}
}
@ui.Body() {
}
</sl-tab-group>
}
}

View File

@@ -0,0 +1,168 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package views
//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/sonr-io/motr/internal/ui"
)
func LoginView() 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_Var3 := 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 = ui.MetaComponent(meta).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var5 := 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 = ui.NavLogo("Motr").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.NavLeft().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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)
return nil
})
templ_7745c5c3_Err = ui.NavRight().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Nav().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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)
return nil
})
templ_7745c5c3_Err = ui.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.HTML().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,28 @@
package views
import (
"github.com/sonr-io/motr/internal/ui"
"github.com/sonr-io/motr/components/inputs"
)
templ RegisterView() {
@ui.HTML() {
@ui.Head() {
@ui.MetaComponent(meta)
}
<sl-tab-group>
@ui.Nav() {
@ui.NavLeft() {
@ui.NavLogo("Motr")
}
@ui.NavRight() {
}
}
@ui.Body() {
@ui.Container() {
@inputs.HandleInitial()
}
}
</sl-tab-group>
}
}

View File

@@ -0,0 +1,191 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package views
//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/sonr-io/motr/components/inputs"
"github.com/sonr-io/motr/internal/ui"
)
func RegisterView() 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_Var3 := 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 = ui.MetaComponent(meta).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var5 := 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 = ui.NavLogo("Motr").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.NavLeft().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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)
return nil
})
templ_7745c5c3_Err = ui.NavRight().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Nav().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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_Var8 := 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 = inputs.HandleInitial().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</sl-tab-group>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = ui.HTML().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,48 @@
package account
import "github.com/sonr-io/motr/internal/sink/models"
templ cardComponent(handle, addr, block, name string) {
<div class="profile-card min-w-[320px]">
<div class="text-white max-w-xs my-auto mx-auto bg-gradient-to-r from-cyan-600 to-cyan-300 p-4 py-5 px-5 rounded-xl">
<div class="flex justify-between">
<div>
<h2>sonr-testnet-1</h2>
<p class="text-2xl font-bold">{ handle }</p>
</div>
<div class="flex items-center opacity-60">
<sl-icon style="font-size: 52px;" library="sonr" name="sonr-fill"></sl-icon>
</div>
</div>
<div class="mt-5 flex justify-between items-center w-52">
<span class="text-lg font-mono">{ addr }</span>
</div>
<div class="flex justify-between mt-5 w-48 ">
<div>
<h3 class="text-xs">Block Created </h3>
<p class="font-bold"><span>#</span>{ block }</p>
</div>
<div>
<h3 class="text-xs">Issued to</h3>
<p class="font-bold">{ name }</p>
</div>
</div>
</div>
</div>
}
// option is a single option in the dropdown
templ optionComponent(account models.Account) {
<div class="flex justify-between items-center">
{ account.Address }
<sl-icon style="font-size: 24px;" library="sonr" name="chevron-right"></sl-icon>
</div>
}
// row is a single row in the list with a basic card
templ rowComponent(account models.Account) {
<div class="flex justify-between items-center">
{ account.Address }
<sl-icon style="font-size: 24px;" library="sonr" name="chevron-right"></sl-icon>
</div>
}

View File

@@ -0,0 +1,180 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package account
//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/sonr-io/motr/internal/sink/models"
func cardComponent(handle, addr, block, name 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_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, "<div class=\"profile-card min-w-[320px]\"><div class=\"text-white max-w-xs my-auto mx-auto bg-gradient-to-r from-cyan-600 to-cyan-300 p-4 py-5 px-5 rounded-xl\"><div class=\"flex justify-between\"><div><h2>sonr-testnet-1</h2><p class=\"text-2xl font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(handle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 11, Col: 43}
}
_, 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, "</p></div><div class=\"flex items-center opacity-60\"><sl-icon style=\"font-size: 52px;\" library=\"sonr\" name=\"sonr-fill\"></sl-icon></div></div><div class=\"mt-5 flex justify-between items-center w-52\"><span class=\"text-lg font-mono\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(addr)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 18, Col: 42}
}
_, 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=\"flex justify-between mt-5 w-48 \"><div><h3 class=\"text-xs\">Block Created </h3><p class=\"font-bold\"><span>#</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(block)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 23, Col: 47}
}
_, 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, "</p></div><div><h3 class=\"text-xs\">Issued to</h3><p class=\"font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 27, Col: 32}
}
_, 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, "</p></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// option is a single option in the dropdown
func optionComponent(account models.Account) 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_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(account.Address)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 37, Col: 19}
}
_, 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, " <sl-icon style=\"font-size: 24px;\" library=\"sonr\" name=\"chevron-right\"></sl-icon></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// row is a single row in the list with a basic card
func rowComponent(account models.Account) 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, 8, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(account.Address)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/account/card.templ`, Line: 45, Col: 19}
}
_, 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, " <sl-icon style=\"font-size: 24px;\" library=\"sonr\" name=\"chevron-right\"></sl-icon></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,63 @@
package account
import (
"strconv"
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type AccountEntity interface {
GetModel() models.Account
GetCard() templ.Component
shortAddr() string
}
type AccountsEntity interface {
GetModels() []models.Account
GetList() templ.Component
GetDropdown() templ.Component
}
func NewAccountEntity(account models.Account) AccountEntity {
return &accountEntity{Account: account}
}
func NewAccountsEntity(accounts []models.Account) AccountsEntity {
return &accountsEntity{Accounts: accounts}
}
type accountEntity struct {
models.Account
}
func (a *accountEntity) shortAddr() string {
if len(a.Address) <= 20 {
return a.Address
}
return a.Address[:16] + "..." + a.Address[len(a.Address)-4:]
}
func (a *accountEntity) GetModel() models.Account {
return a.Account
}
func (a *accountEntity) GetCard() templ.Component {
return cardComponent(a.Handle, a.shortAddr(), strconv.FormatInt(a.BlockCreated, 10), a.Label)
}
type accountsEntity struct {
Accounts []models.Account
}
func (a *accountsEntity) GetModels() []models.Account {
return a.Accounts
}
func (a *accountsEntity) GetList() templ.Component {
return nil
}
func (a *accountsEntity) GetDropdown() templ.Component {
return nil
}

View File

@@ -0,0 +1,11 @@
package account
templ listComponent(accounts AccountsEntity) {
<div class="flex flex-col gap-4 mt-4">
for _,account := range accounts.GetModels() {
<div class="flex justify-between items-center">
@rowComponent(account)
</div>
}
</div>
}

View File

@@ -0,0 +1,58 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package account
//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 listComponent(accounts AccountsEntity) 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, "<div class=\"flex flex-col gap-4 mt-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, account := range accounts.GetModels() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = rowComponent(account).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,32 @@
package account
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetAccountByID(ctx context.Context, id string) (models.Account, error)
GetAccountByAddress(ctx context.Context, address string) (models.Account, error)
GetAccountByController(ctx context.Context, controller string) (models.Account, error)
GetAccountByPublicKey(ctx context.Context, publicKey string) (models.Account, error)
GetAccountByNumber(ctx context.Context, number int64) (models.Account, error)
GetAccountBySequence(ctx context.Context, sequence int64) (models.Account, error)
GetAccountsByChainID(ctx context.Context, chainID string) ([]models.Account, error)
GetAccountsByHandle(ctx context.Context, handle string) ([]models.Account, error)
GetAccountsByLabel(ctx context.Context, name string) ([]models.Account, error)
UpdateAccountLabel(ctx context.Context, arg models.UpdateAccountLabelParams) (models.Account, error)
UpdateAccountSequence(ctx context.Context, arg models.UpdateAccountSequenceParams) (models.Account, error)
SoftDeleteAccount(ctx context.Context, id string) error
ListValidatorAccounts(ctx context.Context) ([]models.Account, error)
ListDelegatorAccounts(ctx context.Context) ([]models.Account, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,11 @@
package account
templ selectComponent(accounts AccountsEntity) {
<div class="flex flex-col gap-4 mt-4">
for _,account := range accounts.GetModels() {
<div class="flex justify-between items-center">
@optionComponent(account)
</div>
}
</div>
}

View File

@@ -0,0 +1,58 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package account
//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 selectComponent(accounts AccountsEntity) 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, "<div class=\"flex flex-col gap-4 mt-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, account := range accounts.GetModels() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = optionComponent(account).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,48 @@
package activity
import "github.com/sonr-io/motr/internal/sink/models"
templ cardComponent(handle, addr, block, name string) {
<div class="profile-card min-w-[320px]">
<div class="text-white max-w-xs my-auto mx-auto bg-gradient-to-r from-cyan-600 to-cyan-300 p-4 py-5 px-5 rounded-xl">
<div class="flex justify-between">
<div>
<h2>sonr-testnet-1</h2>
<p class="text-2xl font-bold">{ handle }</p>
</div>
<div class="flex items-center opacity-60">
<sl-icon style="font-size: 52px;" library="sonr" name="sonr-fill"></sl-icon>
</div>
</div>
<div class="mt-5 flex justify-between items-center w-52">
<span class="text-lg font-mono">{ addr }</span>
</div>
<div class="flex justify-between mt-5 w-48 ">
<div>
<h3 class="text-xs">Block Created </h3>
<p class="font-bold"><span>#</span>{ block }</p>
</div>
<div>
<h3 class="text-xs">Issued to</h3>
<p class="font-bold">{ name }</p>
</div>
</div>
</div>
</div>
}
// option is a single option in the dropdown
templ optionComponent(account models.Activity) {
<div class="flex justify-between items-center">
{ account.Status }
<sl-icon style="font-size: 24px;" library="sonr" name="chevron-right"></sl-icon>
</div>
}
// row is a single row in the list with a basic card
templ rowComponent(account models.Activity) {
<div class="flex justify-between items-center">
{ account.Status }
<sl-icon style="font-size: 24px;" library="sonr" name="chevron-right"></sl-icon>
</div>
}

View File

@@ -0,0 +1,180 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package activity
//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/sonr-io/motr/internal/sink/models"
func cardComponent(handle, addr, block, name 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_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, "<div class=\"profile-card min-w-[320px]\"><div class=\"text-white max-w-xs my-auto mx-auto bg-gradient-to-r from-cyan-600 to-cyan-300 p-4 py-5 px-5 rounded-xl\"><div class=\"flex justify-between\"><div><h2>sonr-testnet-1</h2><p class=\"text-2xl font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(handle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 11, Col: 43}
}
_, 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, "</p></div><div class=\"flex items-center opacity-60\"><sl-icon style=\"font-size: 52px;\" library=\"sonr\" name=\"sonr-fill\"></sl-icon></div></div><div class=\"mt-5 flex justify-between items-center w-52\"><span class=\"text-lg font-mono\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(addr)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 18, Col: 42}
}
_, 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=\"flex justify-between mt-5 w-48 \"><div><h3 class=\"text-xs\">Block Created </h3><p class=\"font-bold\"><span>#</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(block)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 23, Col: 47}
}
_, 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, "</p></div><div><h3 class=\"text-xs\">Issued to</h3><p class=\"font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 27, Col: 32}
}
_, 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, "</p></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// option is a single option in the dropdown
func optionComponent(account models.Activity) 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_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(account.Status)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 37, Col: 18}
}
_, 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, " <sl-icon style=\"font-size: 24px;\" library=\"sonr\" name=\"chevron-right\"></sl-icon></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// row is a single row in the list with a basic card
func rowComponent(account models.Activity) 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, 8, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(account.Status)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `controllers/activity/card.templ`, Line: 45, Col: 18}
}
_, 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, " <sl-icon style=\"font-size: 24px;\" library=\"sonr\" name=\"chevron-right\"></sl-icon></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,16 @@
package activity
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type ActivityEntity interface {
GetModel() models.Activity
GetCard() templ.Component
}
type ActivitiesEntity interface {
GetModels() []models.Activity
GetList() templ.Component
}

View File

@@ -0,0 +1,11 @@
package activity
templ listComponent(accounts ActivitiesEntity) {
<div class="flex flex-col gap-4 mt-4">
for _,account := range accounts.GetModels() {
<div class="flex justify-between items-center">
@rowComponent(account)
</div>
}
</div>
}

View File

@@ -0,0 +1,58 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package activity
//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 listComponent(accounts ActivitiesEntity) 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, "<div class=\"flex flex-col gap-4 mt-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, account := range accounts.GetModels() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex justify-between items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = rowComponent(account).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,27 @@
package activity
import (
"context"
"database/sql"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetActivityByID(ctx context.Context, id string) (models.Activity, error)
GetActivityByTxHash(ctx context.Context, txHash sql.NullString) (models.Activity, error)
InsertActivity(ctx context.Context, arg models.InsertActivityParams) (models.Activity, error)
ListActivitiesByAccount(ctx context.Context, arg models.ListActivitiesByAccountParams) ([]models.Activity, error)
ListActivitiesByStatus(ctx context.Context, arg models.ListActivitiesByStatusParams) ([]models.Activity, error)
ListActivitiesByType(ctx context.Context, arg models.ListActivitiesByTypeParams) ([]models.Activity, error)
SoftDeleteActivity(ctx context.Context, id string) error
UpdateActivityStatus(ctx context.Context, arg models.UpdateActivityStatusParams) (models.Activity, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,17 @@
package asset
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type AssetEntity interface {
GetModel() models.Asset
GetCard(ticker, price string) templ.Component
}
type AssetsEntity interface {
GetModels() []models.Asset
GetList() templ.Component
GetDropdown() templ.Component
}

View File

@@ -0,0 +1,27 @@
package asset
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetAssetByChainAndSymbol(ctx context.Context, arg models.GetAssetByChainAndSymbolParams) (models.Asset, error)
GetAssetByID(ctx context.Context, id string) (models.Asset, error)
GetAssetBySymbol(ctx context.Context, symbol string) (models.Asset, error)
GetAssetWithLatestPrice(ctx context.Context, id string) (models.GetAssetWithLatestPriceRow, error)
InsertAsset(ctx context.Context, arg models.InsertAssetParams) (models.Asset, error)
ListAssetsByChain(ctx context.Context, chainID string) ([]models.Asset, error)
ListAssetsWithLatestPrices(ctx context.Context, arg models.ListAssetsWithLatestPricesParams) ([]models.ListAssetsWithLatestPricesRow, error)
SoftDeleteAsset(ctx context.Context, id string) error
UpdateAsset(ctx context.Context, arg models.UpdateAssetParams) (models.Asset, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,7 @@
package asset
templ selectComponent() {
<div class="flex flex-col gap-4 mt-4">
<div class="flex justify-between items-center"></div>
</div>
}

View File

@@ -1,14 +1,14 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package view
package asset
//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 registerView(data any) templ.Component {
func selectComponent() 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 {
@@ -29,7 +29,7 @@ func registerView(data any) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div><h1>Register</h1></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-col gap-4 mt-4\"><div class=\"flex justify-between items-center\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -0,0 +1,17 @@
package blockchain
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type BlockchainEntity interface {
GetModel() models.Blockchain
GetCard(ticker, price string) templ.Component
}
type BlockchainsEntity interface {
GetModels() []models.Blockchain
GetList() templ.Component
GetDropdown() templ.Component
}

View File

@@ -0,0 +1,41 @@
package blockchain
import (
"context"
"database/sql"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetBlockchainByChainName(ctx context.Context, chainName string) (models.Blockchain, error)
GetBlockchainByCosmosChainID(ctx context.Context, chainIDCosmos sql.NullString) (models.Blockchain, error)
GetBlockchainByEvmChainID(ctx context.Context, chainIDEvm sql.NullString) (models.Blockchain, error)
GetBlockchainByID(ctx context.Context, id string) (models.Blockchain, error)
GetBlockchainEndpoints(ctx context.Context, id string) (models.GetBlockchainEndpointsRow, error)
GetBlockchainExplorer(ctx context.Context, id string) (models.GetBlockchainExplorerRow, error)
GetBlockchainWithAssetInfo(ctx context.Context, id string) (models.GetBlockchainWithAssetInfoRow, error)
InsertBlockchain(ctx context.Context, arg models.InsertBlockchainParams) (models.Blockchain, error)
ListBlockchainsWithAssetInfo(ctx context.Context, arg models.ListBlockchainsWithAssetInfoParams) ([]models.ListBlockchainsWithAssetInfoRow, error)
ListBlockchainsWithERC20Support(ctx context.Context) ([]models.Blockchain, error)
ListBlockchainsWithExtensionSupport(ctx context.Context) ([]models.Blockchain, error)
ListBlockchainsWithMobileSupport(ctx context.Context) ([]models.Blockchain, error)
ListBlockchainsWithStaking(ctx context.Context) ([]models.Blockchain, error)
SearchBlockchains(ctx context.Context, arg models.SearchBlockchainsParams) ([]models.Blockchain, error)
SoftDeleteBlockchain(ctx context.Context, id string) error
UpdateBlockchain(ctx context.Context, arg models.UpdateBlockchainParams) (models.Blockchain, error)
UpdateBlockchainDescriptions(ctx context.Context, arg models.UpdateBlockchainDescriptionsParams) (models.Blockchain, error)
UpdateBlockchainEndpoints(ctx context.Context, arg models.UpdateBlockchainEndpointsParams) (models.Blockchain, error)
UpdateBlockchainExplorer(ctx context.Context, arg models.UpdateBlockchainExplorerParams) (models.Blockchain, error)
UpdateBlockchainFeeInfo(ctx context.Context, arg models.UpdateBlockchainFeeInfoParams) (models.Blockchain, error)
UpdateBlockchainImages(ctx context.Context, arg models.UpdateBlockchainImagesParams) (models.Blockchain, error)
UpdateBlockchainSocialLinks(ctx context.Context, arg models.UpdateBlockchainSocialLinksParams) (models.Blockchain, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,43 @@
package credential
import "github.com/sonr-io/motr/internal/sink/models"
// Define the credential structure matching our frontend data
type CredentialDescriptor struct {
ID string `json:"id"`
RawID string `json:"rawId"`
Type string `json:"type"`
AuthenticatorAttachment string `json:"authenticatorAttachment"`
Transports string `json:"transports"`
ClientExtensionResults map[string]string `json:"clientExtensionResults"`
Response struct {
AttestationObject string `json:"attestationObject"`
ClientDataJSON string `json:"clientDataJSON"`
} `json:"response"`
}
func (c *CredentialDescriptor) ToModel(handle, origin string) *models.Credential {
return &models.Credential{
Handle: handle,
Origin: origin,
CredentialID: c.ID,
Type: c.Type,
Transports: c.Transports,
AuthenticatorAttachment: c.AuthenticatorAttachment,
}
}
func CredentialArrayToDescriptors(credentials []models.Credential) []*CredentialDescriptor {
var descriptors []*CredentialDescriptor
for _, cred := range credentials {
cd := &CredentialDescriptor{
ID: cred.CredentialID,
RawID: cred.CredentialID,
Type: cred.Type,
AuthenticatorAttachment: cred.AuthenticatorAttachment,
Transports: cred.Transports,
}
descriptors = append(descriptors, cd)
}
return descriptors
}

View File

@@ -0,0 +1,18 @@
package credential
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type CredentialEntity interface {
GetModel() models.Credential
GetDescriptor() *CredentialDescriptor
GetInfoModal() templ.Component
}
type CredentialsEntity interface {
GetModels() []models.Credential
GetDescriptors() []*CredentialDescriptor
GetList() templ.Component
}

View File

@@ -0,0 +1,111 @@
package credential
templ PasskeyInitial(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-neutral-500"></sl-icon>
Register Passkey
</sl-button>
}
templ PasskeyError(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-red-500"></sl-icon>
Register Passkey
</sl-button>
}
templ PasskeySuccess(clickHandler templ.ComponentScript) {
<sl-button style="width: 100%;" onclick={ clickHandler }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-green-500"></sl-icon>
Register Passkey
</sl-button>
}
script NavigatorCredentialsCreate(userId string, userHandle string, challenge string) {
const publicKey = {
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
rp: {
name: "Sonr.ID",
},
user: {
// Assuming that userId is ASCII-only
id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
name: userId,
displayName: userHandle,
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7, // "ES256"
},
{
type: "public-key",
alg: -257, // "RS256"
},
],
authenticatorSelection: {
userVerification: "required",
residentKey: "required",
authenticatorAttachment: "platform",
},
timeout: 60000, // 1 minute
extensions: {
payment: {
isPayment: true,
},
largeBlob: {
supported: "preferred",
},
},
};
// Helper function to convert ArrayBuffer to Base64URL string
function arrayBufferToBase64URL(buffer) {
const bytes = new Uint8Array(buffer);
let str = '';
bytes.forEach(byte => { str += String.fromCharCode(byte) });
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
navigator.credentials
.create({ publicKey })
.then((newCredentialInfo) => {
if (!(newCredentialInfo instanceof PublicKeyCredential)) {
throw new Error('Received credential is not a PublicKeyCredential');
}
const response = newCredentialInfo.response;
if (!(response instanceof AuthenticatorAttestationResponse)) {
throw new Error('Response is not an AuthenticatorAttestationResponse');
}
// Convert the credential data to a cross-platform compatible format
const credentialJSON = {
id: newCredentialInfo.id,
rawId: arrayBufferToBase64URL(newCredentialInfo.rawId),
type: newCredentialInfo.type,
authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null,
transports: Array.isArray(response.getTransports) ? response.getTransports() : [],
clientExtensionResults: newCredentialInfo.getClientExtensionResults(),
response: {
attestationObject: arrayBufferToBase64URL(response.attestationObject),
clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON)
}
};
// Set the form value with the stringified credential data
const credential = document.getElementById('credential-data');
credential.value = JSON.stringify(credentialJSON);
// Submit the form
const form = document.getElementById('passkey-form');
form.submit();
})
.catch((err) => {
console.error('Passkey creation failed:', err);
alert(`Failed to create passkey: ${err.message || 'Unknown error'}`);
});
}

View File

@@ -0,0 +1,232 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package credential
//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 PasskeyInitial(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-neutral-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func PasskeyError(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-red-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func PasskeySuccess(clickHandler templ.ComponentScript) 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 = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, clickHandler)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.ComponentScript = clickHandler
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><sl-icon slot=\"prefix\" name=\"passkey\" library=\"sonr\" style=\"font-size: 24px;\" class=\"text-green-500\"></sl-icon> Register Passkey</sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func NavigatorCredentialsCreate(userId string, userHandle string, challenge string) templ.ComponentScript {
return templ.ComponentScript{
Name: `__templ_NavigatorCredentialsCreate_8f96`,
Function: `function __templ_NavigatorCredentialsCreate_8f96(userId, userHandle, challenge){const publicKey = {
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
rp: {
name: "Sonr.ID",
},
user: {
// Assuming that userId is ASCII-only
id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
name: userId,
displayName: userHandle,
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7, // "ES256"
},
{
type: "public-key",
alg: -257, // "RS256"
},
],
authenticatorSelection: {
userVerification: "required",
residentKey: "required",
authenticatorAttachment: "platform",
},
timeout: 60000, // 1 minute
extensions: {
payment: {
isPayment: true,
},
largeBlob: {
supported: "preferred",
},
},
};
// Helper function to convert ArrayBuffer to Base64URL string
function arrayBufferToBase64URL(buffer) {
const bytes = new Uint8Array(buffer);
let str = '';
bytes.forEach(byte => { str += String.fromCharCode(byte) });
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
navigator.credentials
.create({ publicKey })
.then((newCredentialInfo) => {
if (!(newCredentialInfo instanceof PublicKeyCredential)) {
throw new Error('Received credential is not a PublicKeyCredential');
}
const response = newCredentialInfo.response;
if (!(response instanceof AuthenticatorAttestationResponse)) {
throw new Error('Response is not an AuthenticatorAttestationResponse');
}
// Convert the credential data to a cross-platform compatible format
const credentialJSON = {
id: newCredentialInfo.id,
rawId: arrayBufferToBase64URL(newCredentialInfo.rawId),
type: newCredentialInfo.type,
authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null,
transports: Array.isArray(response.getTransports) ? response.getTransports() : [],
clientExtensionResults: newCredentialInfo.getClientExtensionResults(),
response: {
attestationObject: arrayBufferToBase64URL(response.attestationObject),
clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON)
}
};
// Set the form value with the stringified credential data
const credential = document.getElementById('credential-data');
credential.value = JSON.stringify(credentialJSON);
// Submit the form
const form = document.getElementById('passkey-form');
form.submit();
})
.catch((err) => {
console.error('Passkey creation failed:', err);
alert(` + "`" + `Failed to create passkey: ${err.message || 'Unknown error'}` + "`" + `);
});
}`,
Call: templ.SafeScript(`__templ_NavigatorCredentialsCreate_8f96`, userId, userHandle, challenge),
CallInline: templ.SafeScriptInline(`__templ_NavigatorCredentialsCreate_8f96`, userId, userHandle, challenge),
}
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,22 @@
package credential
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetCredentialByID(ctx context.Context, credentialID string) (models.Credential, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]models.Credential, error)
InsertCredential(ctx context.Context, arg models.InsertCredentialParams) (models.Credential, error)
SoftDeleteCredential(ctx context.Context, credentialID string) error
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,16 @@
package health
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type HealthEntity interface {
GetModel() models.Health
GetCard() templ.Component
}
type HealthsEntity interface {
GetModels() []models.Health
GetList() templ.Component
}

View File

@@ -0,0 +1,26 @@
package health
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetHealthByEndpoint(ctx context.Context, endpointUrl string) (models.Health, error)
GetHealthByID(ctx context.Context, id string) (models.Health, error)
InsertHealth(ctx context.Context, arg models.InsertHealthParams) (models.Health, error)
ListHealthByChain(ctx context.Context, arg models.ListHealthByChainParams) ([]models.Health, error)
ListHealthByStatus(ctx context.Context, arg models.ListHealthByStatusParams) ([]models.Health, error)
ListHealthChecksNeedingUpdate(ctx context.Context, limit int64) ([]models.Health, error)
SoftDeleteHealth(ctx context.Context, id string) error
UpdateHealthCheck(ctx context.Context, arg models.UpdateHealthCheckParams) (models.Health, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,16 @@
package prices
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type PriceEntity interface {
GetModel() models.Price
GetCard() templ.Component
}
type PricesEntity interface {
GetModels() []models.Price
GetList() templ.Component
}

View File

@@ -0,0 +1,23 @@
package prices
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetPriceByAssetID(ctx context.Context, assetID string) (models.Price, error)
GetPriceByID(ctx context.Context, id string) (models.Price, error)
InsertPrice(ctx context.Context, arg models.InsertPriceParams) (models.Price, error)
ListPriceHistoryByAssetID(ctx context.Context, arg models.ListPriceHistoryByAssetIDParams) ([]models.Price, error)
UpdatePrice(ctx context.Context, arg models.UpdatePriceParams) (models.Price, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,16 @@
package profile
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type ProfileEntity interface {
GetModel() models.Profile
}
type ProfilesEntity interface {
GetModels() []models.Profile
GetList() templ.Component
GetDropdown() templ.Component
}

View File

@@ -0,0 +1,8 @@
package profile
templ InputAvatar(value string, helpText string) {
<input type="file" name="avatar" id="avatar" name="avatar" accept="image/*"/>
<sl-button variant="neutral" slot="suffix" type="button">
<sl-icon name="upload-2-fill" slot="prefix"></sl-icon>
</sl-button>
}

View File

@@ -1,14 +1,14 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package view
package profile
//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 loginView(data any) templ.Component {
func InputAvatar(value string, helpText 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 {
@@ -29,7 +29,7 @@ func loginView(data any) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div><h1>Login</h1></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<input type=\"file\" name=\"avatar\" id=\"avatar\" name=\"avatar\" accept=\"image/*\"> <sl-button variant=\"neutral\" slot=\"suffix\" type=\"button\"><sl-icon name=\"upload-2-fill\" slot=\"prefix\"></sl-icon></sl-button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -0,0 +1,26 @@
package profile
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
CheckHandleExists(ctx context.Context, handle string) (bool, error)
GetAccountsByHandle(ctx context.Context, handle string) ([]models.Account, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]models.Credential, error)
GetVaultsByHandle(ctx context.Context, handle string) ([]models.Vault, error)
InsertProfile(ctx context.Context, arg models.InsertProfileParams) (models.Profile, error)
ListProfiles(ctx context.Context, arg models.ListProfilesParams) ([]models.Profile, error)
SoftDeleteProfile(ctx context.Context, address string) error
UpdateProfile(ctx context.Context, arg models.UpdateProfileParams) (models.Profile, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,16 @@
package service
import (
"github.com/a-h/templ"
"github.com/sonr-io/motr/internal/sink/models"
)
type ServiceEntity interface {
GetModel() models.Service
GetCard() templ.Component
}
type ServicesEntity interface {
GetModels() []models.Service
GetList() templ.Component
}

View File

@@ -0,0 +1,26 @@
package service
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetServiceByAddress(ctx context.Context, address string) (models.Service, error)
GetServiceByChainAndAddress(ctx context.Context, arg models.GetServiceByChainAndAddressParams) (models.Service, error)
GetServiceByID(ctx context.Context, id string) (models.Service, error)
InsertService(ctx context.Context, arg models.InsertServiceParams) (models.Service, error)
ListServicesByChain(ctx context.Context, arg models.ListServicesByChainParams) ([]models.Service, error)
ListServicesByOwner(ctx context.Context, arg models.ListServicesByOwnerParams) ([]models.Service, error)
SoftDeleteService(ctx context.Context, id string) error
UpdateService(ctx context.Context, arg models.UpdateServiceParams) (models.Service, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

View File

@@ -0,0 +1,9 @@
package vault
import (
"github.com/sonr-io/motr/internal/sink/models"
)
type VaultEntity interface {
GetModel() models.Vault
}

View File

@@ -0,0 +1,7 @@
package vault
templ LoginForm(opts LoginOptions) {
<form method="post" action="/login">
<sl-button type="submit" variant="primary">Login</sl-button>
</form>
}

View File

@@ -1,15 +1,14 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
package view
package vault
//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 authorizeView(data any) templ.Component {
func LoginForm(opts LoginOptions) 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 {
@@ -30,7 +29,7 @@ func authorizeView(data any) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div><h1>Authorize</h1></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<form method=\"post\" action=\"/login\"><sl-button type=\"submit\" variant=\"primary\">Login</sl-button></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -0,0 +1,18 @@
package vault
import "github.com/sonr-io/motr/controllers/credential"
type LoginOptions struct {
Account string
Handle string
HelpText string
Label string
Challenge string
AllowedCredentials []*credential.CredentialDescriptor
}
type RegisterOptions struct {
Address string
Handle string
Challenge string
}

View File

@@ -0,0 +1,25 @@
package vault
import (
"context"
"github.com/sonr-io/motr/internal/sink/models"
)
type Queries interface {
GetVaultByID(ctx context.Context, id string) (models.Vault, error)
GetVaultConfigByCID(ctx context.Context, cid string) (models.Vault, error)
GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error)
GetVaultsByHandle(ctx context.Context, handle string) ([]models.Vault, error)
InsertVault(ctx context.Context, arg models.InsertVaultParams) (models.Vault, error)
SoftDeleteVault(ctx context.Context, id string) error
UpdateVault(ctx context.Context, arg models.UpdateVaultParams) (models.Vault, error)
}
func NewQueries(q models.Querier) Queries {
return &queries{Querier: q}
}
type queries struct {
models.Querier
}

24
go.mod
View File

@@ -1,33 +1,33 @@
module github.com/onsonr/motr
module github.com/sonr-io/motr
go 1.23.1
go 1.24.2
require (
github.com/a-h/templ v0.3.857
github.com/go-webauthn/webauthn v0.12.3
github.com/labstack/echo/v4 v4.13.3
github.com/ncruces/go-sqlite3 v0.21.3
github.com/nlepage/go-wasm-http-server/v2 v2.2.1
github.com/segmentio/ksuid v1.0.4
github.com/syumai/workers v0.30.2
)
require (
github.com/hack-pad/safejs v0.1.1 // indirect
github.com/nlepage/go-js-promise v1.0.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-webauthn/x v0.1.20 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
)
require (
// github.com/a-h/templ v0.3.857 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/tetratelabs/wazero v1.8.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.8.0 // indirect
)

42
go.sum
View File

@@ -1,11 +1,21 @@
github.com/a-h/templ v0.3.857 h1:6EqcJuGZW4OL+2iZ3MD+NnIcG7nGkaQeF2Zq5kf9ZGg=
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
github.com/go-webauthn/x v0.1.20/go.mod h1:n/gAc8ssZJGATM0qThE+W+vfgXiMedsWi3wf/C4lld0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
@@ -15,24 +25,22 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-sqlite3 v0.21.3 h1:hHkfNQLcbnxPJZhC/RGw9SwP3bfkv/Y0xUHWsr1CdMQ=
github.com/ncruces/go-sqlite3 v0.21.3/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/nlepage/go-js-promise v1.0.0 h1:K7OmJ3+0BgWJ2LfXchg2sI6RDr7AW/KWR8182epFwGQ=
github.com/nlepage/go-js-promise v1.0.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo=
github.com/nlepage/go-wasm-http-server/v2 v2.2.1 h1:4tzhSb3HKQ3Ykt2TPfqEnmcPfw8n1E8agv4OzAyckr8=
github.com/nlepage/go-wasm-http-server/v2 v2.2.1/go.mod h1:r8j7cEOeUqNp+c+C52sNuWaFTvvT/cNqIwBuEtA36HA=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4=
github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
github.com/syumai/workers v0.30.2 h1:ZefPdAoXBsw87Bxy1LTAR6Pm9Gbxw/iM7DNraPSput0=
github.com/syumai/workers v0.30.2/go.mod h1:ZnqmdiHNBrbxOLrZ/HJ5jzHy6af9cmiNZk10R9NrIEA=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
@@ -43,7 +51,5 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

171
handlers/auth/controller.go Normal file
View File

@@ -0,0 +1,171 @@
//go:build js && wasm
// +build js,wasm
package auth
import (
"context"
"encoding/json"
"fmt"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/components/views"
"github.com/sonr-io/motr/internal/config"
"github.com/sonr-io/motr/internal/middleware"
"github.com/sonr-io/motr/internal/sink/models"
"github.com/syumai/workers/cloudflare/kv"
)
// ╭───────────────────────────────────────────────────────────╮
// │ Auth Handler │
// ╰───────────────────────────────────────────────────────────╯
func Register(cfg config.Config, s *config.Server) error {
q, err := cfg.DB.GetQuerier()
if err != nil {
return err
}
hkv, err := cfg.KV.GetHandles()
if err != nil {
return err
}
skv, err := cfg.KV.GetSessions()
if err != nil {
return err
}
h := NewAuthHandler(q, hkv, skv)
h.SetupRoutes(s)
return nil
}
// ╭───────────────────────────────────────────────────────────╮
// │ Auth Handler │
// ╰───────────────────────────────────────────────────────────╯
// AuthHandler is the auth handler.
type AuthHandler struct {
DB models.Querier
Handles *kv.Namespace
Sessions *kv.Namespace
}
func NewAuthHandler(q models.Querier, hkv *kv.Namespace, skv *kv.Namespace) *AuthHandler {
return &AuthHandler{DB: q, Handles: hkv, Sessions: skv}
}
// SetupRoutes sets up the routes for the auth handler.
func (h *AuthHandler) SetupRoutes(s *config.Server) {
s.GET("/login", h.HandleLoginInitial)
s.GET("/login/:handle", h.HandleLoginStart)
s.POST("/login/:handle/check", h.HandleLoginUsernameCheck)
s.POST("/login/:handle/finish", h.HandleLoginFinish)
s.GET("/register", h.HandleRegisterInitial)
s.GET("/register/:handle", h.HandleRegisterStart)
s.POST("/register/:handle/check", h.HandleRegisterUsernameCheck)
s.POST("/register/:handle/finish", h.HandleRegisterFinish)
}
func (c *AuthHandler) VerifyHandle(handle string, target bool) bool {
_, err := c.Handles.GetString(handle, nil)
if err != nil {
return !target
}
res, err := c.DB.CheckHandleExists(context.Background(), handle)
if err != nil {
return false
}
return res == target
}
// ╭───────────────────────────────────────────────────────────╮
// │ Login Handlers (/login) │
// ╰───────────────────────────────────────────────────────────╯
// HandleLoginFinish handles the finish login request.
func (h *AuthHandler) HandleLoginFinish(c echo.Context) error {
return middleware.Render(c, views.LoginView())
}
// HandleLoginStart handles the start login request.
func (h *AuthHandler) HandleLoginStart(c echo.Context) error {
return middleware.Render(c, views.LoginView())
}
func (h *AuthHandler) HandleLoginInitial(c echo.Context) error {
return middleware.Render(c, views.LoginView())
}
// HandleSubmitCredentialLogin handles the submit credential login request.
func (h *AuthHandler) HandleSubmitCredentialLogin(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
// ╭───────────────────────────────────────────────────────────╮
// │ Profile Handlers (/login) │
// ╰───────────────────────────────────────────────────────────╯
// HandleRegisterUsernameCheck handles the username check request.
func (h *AuthHandler) HandleRegisterUsernameCheck(c echo.Context) error {
handle := c.FormValue("handle")
ok := h.VerifyHandle(handle, false)
if ok {
return middleware.Render(c, views.RegisterView())
}
return middleware.Render(c, views.RegisterView())
}
// HandleLoginUsernameCheck handles the username check request.
func (h *AuthHandler) HandleLoginUsernameCheck(c echo.Context) error {
handle := c.FormValue("handle")
ok := h.VerifyHandle(handle, true)
if ok {
return middleware.Render(c, views.RegisterView())
}
return middleware.Render(c, views.RegisterView())
}
func (h *AuthHandler) HandleSubmitUsernameClaim(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
func (h *AuthHandler) HandleSubmitProfile(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
// ╭────────────────────────────────────────────────────────╮
// │ Register Init (/register) │
// ╰────────────────────────────────────────────────────────╯
// HandleRegisterInitial handles the initial register request.
func (h *AuthHandler) HandleRegisterInitial(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
// HandleRegisterStart handles the start register request.
func (h *AuthHandler) HandleRegisterStart(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
// HandleRegisterFinish handles the finish register request.
func (h *AuthHandler) HandleRegisterFinish(c echo.Context) error {
return middleware.Render(c, views.RegisterView())
}
// HandleSubmitCredentialRegister handles the submit credential register request.
func (h *AuthHandler) HandleSubmitCredentialRegister(c echo.Context) error {
credJSON := c.FormValue("credentialJSON")
if credJSON == "" {
return middleware.Render(c, views.RegisterView())
}
cred := webauthn.Credential{}
err := json.Unmarshal([]byte(credJSON), &cred)
if err != nil {
return middleware.Render(c, views.RegisterView())
}
fmt.Println(cred)
return middleware.Render(c, views.RegisterView())
}

View File

@@ -0,0 +1,52 @@
//go:build js && wasm
// +build js,wasm
package landing
import (
"github.com/labstack/echo/v4"
"github.com/sonr-io/motr/components/views"
"github.com/sonr-io/motr/internal/config"
"github.com/sonr-io/motr/internal/middleware"
"github.com/sonr-io/motr/internal/sink/models"
"github.com/syumai/workers/cloudflare/kv"
)
func Register(cfg config.Config, s *config.Server) error {
q, err := cfg.DB.GetQuerier()
if err != nil {
return err
}
hkv, err := cfg.KV.GetHandles()
if err != nil {
return err
}
skv, err := cfg.KV.GetSessions()
if err != nil {
return err
}
h := New(q, hkv, skv)
h.SetupRoutes(s)
return nil
}
type LandingHandler struct {
DB models.Querier
Handles *kv.Namespace
Sessions *kv.Namespace
}
func New(q models.Querier, hkv *kv.Namespace, skv *kv.Namespace) *LandingHandler {
return &LandingHandler{DB: q, Handles: hkv, Sessions: skv}
}
func (h *LandingHandler) SetupRoutes(s *config.Server) {
s.GET("/", h.HandleIndex)
}
func (h *LandingHandler) HandleIndex(c echo.Context) error {
if err := middleware.GetSession(c).SaveStatus(h.Sessions); err != nil {
return err
}
return middleware.Render(c, views.HomeView())
}

183
internal/config/config.go Normal file
View File

@@ -0,0 +1,183 @@
//go:build js && wasm
// +build js,wasm
package config
import (
"database/sql"
"strconv"
"time"
"github.com/sonr-io/motr/internal/sink/models"
"github.com/syumai/workers/cloudflare"
_ "github.com/syumai/workers/cloudflare/d1"
"github.com/syumai/workers/cloudflare/kv"
)
type MotrMode string
const (
ControllerMode MotrMode = "controller"
ResolverMode MotrMode = "resolver"
)
func (m MotrMode) String() string {
return string(m)
}
type Config struct {
Sonr SonrConfig `json:"sonr"`
IPFS IPFSConfig `json:"ipfs"`
Mode MotrMode `json:"mode"`
DB DBConfig `json:"db"`
Cache CacheConfig `json:"cache"` // Added Cache configuration
KV KVConfig `json:"kv"` // Added KV configuration
}
func Get() Config {
c := Config{
Sonr: getSonrConfig(),
IPFS: getIPFSConfig(),
Mode: getMotrMode(),
DB: getDBConfig(),
Cache: getCacheConfig(), // Added Cache configuration
KV: getKVConfig(), // Added KV configuration
}
return c
}
type SonrConfig struct {
ChainID string `json:"chain_id"`
APIURL string `json:"api_url"`
RPCURL string `json:"rpc_url"`
}
func getSonrConfig() SonrConfig {
return SonrConfig{
ChainID: cloudflare.Getenv("SONR_CHAIN_ID"),
APIURL: cloudflare.Getenv("SONR_API_URL"),
RPCURL: cloudflare.Getenv("SONR_RPC_URL"),
}
}
type IPFSConfig struct {
GatewayURL string `json:"gateway_url"`
}
func getIPFSConfig() IPFSConfig {
return IPFSConfig{
GatewayURL: cloudflare.Getenv("IPFS_GATEWAY"),
}
}
func getMotrMode() MotrMode {
mode := cloudflare.Getenv("MOTR_MODE")
if mode == "" {
return ControllerMode
}
return MotrMode(mode)
}
type DBConfig struct {
DBName string `json:"common_db_name"`
}
func getDBConfig() DBConfig {
return DBConfig{
DBName: "DB",
}
}
func (c DBConfig) GetCommon() (*sql.DB, error) {
return sql.Open("d1", c.DBName)
}
func (c DBConfig) GetQuerier() (models.Querier, error) {
db, err := c.GetCommon()
if err != nil {
return nil, err
}
return models.New(db), nil
}
// CacheConfig defines the configuration for Cloudflare cache
type CacheConfig 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 getCacheConfig() CacheConfig {
// Default values
config := CacheConfig{
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",
},
}
// Override with environment variables if provided
if enabled := cloudflare.Getenv("CACHE_ENABLED"); enabled != "" {
config.Enabled = enabled == "true"
}
if maxAge := cloudflare.Getenv("CACHE_DEFAULT_MAX_AGE"); maxAge != "" {
// Parse the string to int, defaulting to 60 if parsing fails
if parsed, err := strconv.Atoi(maxAge); err == nil {
config.DefaultMaxAge = parsed
}
}
if bypassHeader := cloudflare.Getenv("CACHE_BYPASS_HEADER"); bypassHeader != "" {
config.BypassHeader = bypassHeader
}
if bypassValue := cloudflare.Getenv("CACHE_BYPASS_VALUE"); bypassValue != "" {
config.BypassValue = bypassValue
}
return config
}
type KVConfig struct {
Sessions string `json:"sessions"`
Handles string `json:"handles"`
DefaultExpiry time.Duration `json:"expiry"`
}
func getKVConfig() KVConfig {
return KVConfig{
Sessions: "SESSIONS",
Handles: "HANDLES",
DefaultExpiry: time.Hour * 1, // 1 hour by default
}
}
func (c KVConfig) GetSessionExpiry(t time.Time) int64 {
return c.DefaultExpiry.Nanoseconds() + t.UnixNano()
}
func (c KVConfig) GetSessions() (*kv.Namespace, error) {
return kv.NewNamespace(c.Sessions)
}
func (c KVConfig) GetHandles() (*kv.Namespace, error) {
return kv.NewNamespace(c.Handles)
}

12
internal/config/errors.go Normal file
View File

@@ -0,0 +1,12 @@
package config
import "errors"
func newError(msg string) error {
return errors.New(msg)
}
var (
ErrInvalidMode = newError("invalid mode")
ErrDBNotFound = newError("db not found")
)

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