feat: introduce modular architecture for enhanced feature isolation

This commit is contained in:
2025-04-03 22:39:28 -04:00
parent af732bf208
commit a7ff4ccaf7
62 changed files with 1784 additions and 32 deletions

View File

@@ -3,6 +3,9 @@
version: "3"
silent: true
includes:
generate: { taskfile: ./Generate.yml, flatten: true }
vars:
ROOT_DIR:
sh: git rev-parse --show-toplevel
@@ -27,14 +30,7 @@ tasks:
- defer: rm -rf .task
- task: build:go
# - task: build:docker
gen:
desc: Generates code
cmds:
- defer: rm -rf .task
- task: gen:templ
- task: gen:sqlc
#
clean:
cmds:
- rm -rf bin
@@ -73,30 +69,6 @@ tasks:
cmds:
- go build -o bin/motr.wasm .
gen:templ:
internal: true
dir: "{{.ROOT_DIR}}"
sources:
- "**/*.templ"
generates:
- "**/_templ.go"
cmds:
- templ generate
gen:sqlc:
internal: true
dir: "{{.ROOT_DIR}}/internal/sink"
sources:
- "**/query.sql"
- "**/schema.sql"
generates:
- "**/db.go"
- "**/querier.go"
- "**/models.go"
- "**/query.sql.go"
cmds:
- sqlc generate
test:go:
internal: true
dir: "{{.ROOT_DIR}}"

54
.taskfiles/Generate.yml Normal file
View File

@@ -0,0 +1,54 @@
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"
silent: true
vars:
ROOT_DIR:
sh: git rev-parse --show-toplevel
tasks:
gen:
desc: Generates code
sources:
- "x/account"
- "x/asset"
- "x/balance"
- "x/credential"
- "x/device"
- "x/profile"
- "x/session"
- "x/vault"
cmds:
- defer: rm -rf .task
- task: gen:templ
- for: sources
task: gen:sqlc
vars:
PATH: "{{.ITEM}}"
gen:templ:
internal: true
dir: "{{.ROOT_DIR}}"
sources:
- "**/*.templ"
generates:
- "**/_templ.go"
cmds:
- templ generate
gen:sqlc:
internal: true
requires:
vars: [PATH]
dir: "{{.PATH}}"
sources:
- "**/query.sql"
- "**/schema.sql"
generates:
- "**/db.go"
- "**/querier.go"
- "**/models.go"
- "**/query.sql.go"
cmds:
- sqlc generate

View File

@@ -40,6 +40,20 @@ type Asset struct {
CoingeckoID sql.NullString `json:"coingecko_id"`
}
type Balance struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
AccountID string `json:"account_id"`
AssetID string `json:"asset_id"`
Amount string `json:"amount"`
LastUpdatedHeight int64 `json:"last_updated_height"`
IsDelegated bool `json:"is_delegated"`
IsStaked bool `json:"is_staked"`
IsVesting bool `json:"is_vesting"`
}
type Credential struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
@@ -53,6 +67,27 @@ type Credential struct {
Transports string `json:"transports"`
}
type Device struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
ProfileID string `json:"profile_id"`
CredentialID string `json:"credential_id"`
Name string `json:"name"`
DeviceType string `json:"device_type"`
OsName string `json:"os_name"`
OsVersion string `json:"os_version"`
BrowserName sql.NullString `json:"browser_name"`
BrowserVersion sql.NullString `json:"browser_version"`
LastUsedAt time.Time `json:"last_used_at"`
IsTrusted bool `json:"is_trusted"`
IsCurrent bool `json:"is_current"`
Fingerprint string `json:"fingerprint"`
UserAgent sql.NullString `json:"user_agent"`
IpAddress sql.NullString `json:"ip_address"`
}
type Profile struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`

View File

@@ -10,22 +10,41 @@ import (
type Querier interface {
CheckHandleExists(ctx context.Context, handle string) (bool, error)
// Balance table methods
CreateBalance(ctx context.Context, arg CreateBalanceParams) (Balance, error)
// Device table methods
CreateDevice(ctx context.Context, arg CreateDeviceParams) (Device, error)
CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error)
GetBalanceByAccountAndAsset(ctx context.Context, arg GetBalanceByAccountAndAssetParams) (Balance, error)
GetBalanceByID(ctx context.Context, id string) (Balance, error)
GetChallengeBySessionID(ctx context.Context, id string) (string, error)
GetCredentialByID(ctx context.Context, credentialID string) (Credential, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error)
GetCredentialsByProfile(ctx context.Context, profileID string) ([]Credential, error)
GetDeviceByFingerprint(ctx context.Context, arg GetDeviceByFingerprintParams) (Device, error)
GetDeviceByID(ctx context.Context, id string) (Device, error)
GetHumanVerificationNumbers(ctx context.Context, id string) (GetHumanVerificationNumbersRow, error)
GetProfileByAddress(ctx context.Context, address string) (Profile, error)
GetProfileByHandle(ctx context.Context, handle string) (Profile, error)
GetProfileByID(ctx context.Context, id string) (Profile, error)
GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error)
GetSessionByID(ctx context.Context, id string) (Session, error)
GetTrustedDevicesByProfile(ctx context.Context, profileID string) ([]Device, error)
GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error)
GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error)
InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error)
InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error)
ListBalancesByAccount(ctx context.Context, accountID string) ([]Balance, error)
ListDevicesByProfile(ctx context.Context, profileID string) ([]Device, error)
SoftDeleteBalance(ctx context.Context, id string) error
SoftDeleteCredential(ctx context.Context, credentialID string) error
SoftDeleteDevice(ctx context.Context, id string) error
SoftDeleteProfile(ctx context.Context, address string) error
UpdateBalance(ctx context.Context, arg UpdateBalanceParams) (Balance, error)
// Additional credential methods for better integration with devices
UpdateCredential(ctx context.Context, arg UpdateCredentialParams) (Credential, error)
UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (Device, error)
UpdateDeviceLastUsed(ctx context.Context, arg UpdateDeviceLastUsedParams) (Device, error)
UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error)
UpdateSessionHumanVerification(ctx context.Context, arg UpdateSessionHumanVerificationParams) (Session, error)
UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error)

View File

@@ -7,6 +7,7 @@ package models
import (
"context"
"database/sql"
)
const checkHandleExists = `-- name: CheckHandleExists :one
@@ -22,6 +23,139 @@ func (q *Queries) CheckHandleExists(ctx context.Context, handle string) (bool, e
return handle_exists, err
}
const createBalance = `-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id, created_at, updated_at, deleted_at, account_id, asset_id, amount, last_updated_height, is_delegated, is_staked, is_vesting
`
type CreateBalanceParams struct {
ID string `json:"id"`
AccountID string `json:"account_id"`
AssetID string `json:"asset_id"`
Amount string `json:"amount"`
LastUpdatedHeight int64 `json:"last_updated_height"`
IsDelegated bool `json:"is_delegated"`
IsStaked bool `json:"is_staked"`
IsVesting bool `json:"is_vesting"`
}
// Balance table methods
func (q *Queries) CreateBalance(ctx context.Context, arg CreateBalanceParams) (Balance, error) {
row := q.db.QueryRowContext(ctx, createBalance,
arg.ID,
arg.AccountID,
arg.AssetID,
arg.Amount,
arg.LastUpdatedHeight,
arg.IsDelegated,
arg.IsStaked,
arg.IsVesting,
)
var i Balance
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.AccountID,
&i.AssetID,
&i.Amount,
&i.LastUpdatedHeight,
&i.IsDelegated,
&i.IsStaked,
&i.IsVesting,
)
return i, err
}
const createDevice = `-- name: CreateDevice :one
INSERT INTO devices (
id,
profile_id,
credential_id,
name,
device_type,
os_name,
os_version,
browser_name,
browser_version,
is_trusted,
is_current,
fingerprint,
user_agent,
ip_address
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address
`
type CreateDeviceParams struct {
ID string `json:"id"`
ProfileID string `json:"profile_id"`
CredentialID string `json:"credential_id"`
Name string `json:"name"`
DeviceType string `json:"device_type"`
OsName string `json:"os_name"`
OsVersion string `json:"os_version"`
BrowserName sql.NullString `json:"browser_name"`
BrowserVersion sql.NullString `json:"browser_version"`
IsTrusted bool `json:"is_trusted"`
IsCurrent bool `json:"is_current"`
Fingerprint string `json:"fingerprint"`
UserAgent sql.NullString `json:"user_agent"`
IpAddress sql.NullString `json:"ip_address"`
}
// Device table methods
func (q *Queries) CreateDevice(ctx context.Context, arg CreateDeviceParams) (Device, error) {
row := q.db.QueryRowContext(ctx, createDevice,
arg.ID,
arg.ProfileID,
arg.CredentialID,
arg.Name,
arg.DeviceType,
arg.OsName,
arg.OsVersion,
arg.BrowserName,
arg.BrowserVersion,
arg.IsTrusted,
arg.IsCurrent,
arg.Fingerprint,
arg.UserAgent,
arg.IpAddress,
)
var i Device
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
)
return i, err
}
const createSession = `-- name: CreateSession :one
INSERT INTO sessions (
id,
@@ -99,6 +233,61 @@ func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (S
return i, err
}
const getBalanceByAccountAndAsset = `-- name: GetBalanceByAccountAndAsset :one
SELECT id, created_at, updated_at, deleted_at, account_id, asset_id, amount, last_updated_height, is_delegated, is_staked, is_vesting FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1
`
type GetBalanceByAccountAndAssetParams struct {
AccountID string `json:"account_id"`
AssetID string `json:"asset_id"`
}
func (q *Queries) GetBalanceByAccountAndAsset(ctx context.Context, arg GetBalanceByAccountAndAssetParams) (Balance, error) {
row := q.db.QueryRowContext(ctx, getBalanceByAccountAndAsset, arg.AccountID, arg.AssetID)
var i Balance
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.AccountID,
&i.AssetID,
&i.Amount,
&i.LastUpdatedHeight,
&i.IsDelegated,
&i.IsStaked,
&i.IsVesting,
)
return i, err
}
const getBalanceByID = `-- name: GetBalanceByID :one
SELECT id, created_at, updated_at, deleted_at, account_id, asset_id, amount, last_updated_height, is_delegated, is_staked, is_vesting FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetBalanceByID(ctx context.Context, id string) (Balance, error) {
row := q.db.QueryRowContext(ctx, getBalanceByID, id)
var i Balance
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.AccountID,
&i.AssetID,
&i.Amount,
&i.LastUpdatedHeight,
&i.IsDelegated,
&i.IsStaked,
&i.IsVesting,
)
return i, err
}
const getChallengeBySessionID = `-- name: GetChallengeBySessionID :one
SELECT challenge FROM sessions
WHERE id = ? AND deleted_at IS NULL
@@ -177,6 +366,115 @@ func (q *Queries) GetCredentialsByHandle(ctx context.Context, handle string) ([]
return items, nil
}
const getCredentialsByProfile = `-- name: GetCredentialsByProfile :many
SELECT c.id, c.created_at, c.updated_at, c.deleted_at, c.handle, c.credential_id, c.authenticator_attachment, c.origin, c.type, c.transports FROM credentials c
JOIN devices d ON c.credential_id = d.credential_id
WHERE d.profile_id = ? AND c.deleted_at IS NULL AND d.deleted_at IS NULL
`
func (q *Queries) GetCredentialsByProfile(ctx context.Context, profileID string) ([]Credential, error) {
rows, err := q.db.QueryContext(ctx, getCredentialsByProfile, profileID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Credential
for rows.Next() {
var i Credential
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.CredentialID,
&i.AuthenticatorAttachment,
&i.Origin,
&i.Type,
&i.Transports,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDeviceByFingerprint = `-- name: GetDeviceByFingerprint :one
SELECT id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address FROM devices
WHERE profile_id = ? AND fingerprint = ? AND deleted_at IS NULL
LIMIT 1
`
type GetDeviceByFingerprintParams struct {
ProfileID string `json:"profile_id"`
Fingerprint string `json:"fingerprint"`
}
func (q *Queries) GetDeviceByFingerprint(ctx context.Context, arg GetDeviceByFingerprintParams) (Device, error) {
row := q.db.QueryRowContext(ctx, getDeviceByFingerprint, arg.ProfileID, arg.Fingerprint)
var i Device
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
)
return i, err
}
const getDeviceByID = `-- name: GetDeviceByID :one
SELECT id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address FROM devices
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetDeviceByID(ctx context.Context, id string) (Device, error) {
row := q.db.QueryRowContext(ctx, getDeviceByID, id)
var i Device
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
)
return i, err
}
const getHumanVerificationNumbers = `-- name: GetHumanVerificationNumbers :one
SELECT is_human_first, is_human_last FROM sessions
WHERE id = ? AND deleted_at IS NULL
@@ -324,6 +622,54 @@ func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error
return i, err
}
const getTrustedDevicesByProfile = `-- name: GetTrustedDevicesByProfile :many
SELECT id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address FROM devices
WHERE profile_id = ? AND is_trusted = true AND deleted_at IS NULL
ORDER BY last_used_at DESC
`
func (q *Queries) GetTrustedDevicesByProfile(ctx context.Context, profileID string) ([]Device, error) {
rows, err := q.db.QueryContext(ctx, getTrustedDevicesByProfile, profileID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Device
for rows.Next() {
var i Device
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getVaultConfigByCID = `-- name: GetVaultConfigByCID :one
SELECT id, created_at, updated_at, deleted_at, handle, origin, address, cid, config, session_id, redirect_uri FROM vaults
WHERE cid = ?
@@ -445,6 +791,106 @@ func (q *Queries) InsertProfile(ctx context.Context, arg InsertProfileParams) (P
return i, err
}
const listBalancesByAccount = `-- name: ListBalancesByAccount :many
SELECT id, created_at, updated_at, deleted_at, account_id, asset_id, amount, last_updated_height, is_delegated, is_staked, is_vesting FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id
`
func (q *Queries) ListBalancesByAccount(ctx context.Context, accountID string) ([]Balance, error) {
rows, err := q.db.QueryContext(ctx, listBalancesByAccount, accountID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Balance
for rows.Next() {
var i Balance
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.AccountID,
&i.AssetID,
&i.Amount,
&i.LastUpdatedHeight,
&i.IsDelegated,
&i.IsStaked,
&i.IsVesting,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listDevicesByProfile = `-- name: ListDevicesByProfile :many
SELECT id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address FROM devices
WHERE profile_id = ? AND deleted_at IS NULL
ORDER BY last_used_at DESC
`
func (q *Queries) ListDevicesByProfile(ctx context.Context, profileID string) ([]Device, error) {
rows, err := q.db.QueryContext(ctx, listDevicesByProfile, profileID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Device
for rows.Next() {
var i Device
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const softDeleteBalance = `-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?
`
func (q *Queries) SoftDeleteBalance(ctx context.Context, id string) error {
_, err := q.db.ExecContext(ctx, softDeleteBalance, id)
return err
}
const softDeleteCredential = `-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
@@ -456,6 +902,17 @@ func (q *Queries) SoftDeleteCredential(ctx context.Context, credentialID string)
return err
}
const softDeleteDevice = `-- name: SoftDeleteDevice :exec
UPDATE devices
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?
`
func (q *Queries) SoftDeleteDevice(ctx context.Context, id string) error {
_, err := q.db.ExecContext(ctx, softDeleteDevice, id)
return err
}
const softDeleteProfile = `-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
@@ -467,6 +924,180 @@ func (q *Queries) SoftDeleteProfile(ctx context.Context, address string) error {
return err
}
const updateBalance = `-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING id, created_at, updated_at, deleted_at, account_id, asset_id, amount, last_updated_height, is_delegated, is_staked, is_vesting
`
type UpdateBalanceParams struct {
Amount string `json:"amount"`
LastUpdatedHeight int64 `json:"last_updated_height"`
IsDelegated bool `json:"is_delegated"`
IsStaked bool `json:"is_staked"`
IsVesting bool `json:"is_vesting"`
ID string `json:"id"`
}
func (q *Queries) UpdateBalance(ctx context.Context, arg UpdateBalanceParams) (Balance, error) {
row := q.db.QueryRowContext(ctx, updateBalance,
arg.Amount,
arg.LastUpdatedHeight,
arg.IsDelegated,
arg.IsStaked,
arg.IsVesting,
arg.ID,
)
var i Balance
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.AccountID,
&i.AssetID,
&i.Amount,
&i.LastUpdatedHeight,
&i.IsDelegated,
&i.IsStaked,
&i.IsVesting,
)
return i, err
}
const updateCredential = `-- name: UpdateCredential :one
UPDATE credentials
SET
authenticator_attachment = ?,
transports = ?,
updated_at = CURRENT_TIMESTAMP
WHERE credential_id = ? AND deleted_at IS NULL
RETURNING id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports
`
type UpdateCredentialParams struct {
AuthenticatorAttachment string `json:"authenticator_attachment"`
Transports string `json:"transports"`
CredentialID string `json:"credential_id"`
}
// Additional credential methods for better integration with devices
func (q *Queries) UpdateCredential(ctx context.Context, arg UpdateCredentialParams) (Credential, error) {
row := q.db.QueryRowContext(ctx, updateCredential, arg.AuthenticatorAttachment, arg.Transports, arg.CredentialID)
var i Credential
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.CredentialID,
&i.AuthenticatorAttachment,
&i.Origin,
&i.Type,
&i.Transports,
)
return i, err
}
const updateDevice = `-- name: UpdateDevice :one
UPDATE devices
SET
name = ?,
is_trusted = ?,
is_current = ?,
last_used_at = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address
`
type UpdateDeviceParams struct {
Name string `json:"name"`
IsTrusted bool `json:"is_trusted"`
IsCurrent bool `json:"is_current"`
ID string `json:"id"`
}
func (q *Queries) UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (Device, error) {
row := q.db.QueryRowContext(ctx, updateDevice,
arg.Name,
arg.IsTrusted,
arg.IsCurrent,
arg.ID,
)
var i Device
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
)
return i, err
}
const updateDeviceLastUsed = `-- name: UpdateDeviceLastUsed :one
UPDATE devices
SET
last_used_at = CURRENT_TIMESTAMP,
ip_address = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING id, created_at, updated_at, deleted_at, profile_id, credential_id, name, device_type, os_name, os_version, browser_name, browser_version, last_used_at, is_trusted, is_current, fingerprint, user_agent, ip_address
`
type UpdateDeviceLastUsedParams struct {
IpAddress sql.NullString `json:"ip_address"`
ID string `json:"id"`
}
func (q *Queries) UpdateDeviceLastUsed(ctx context.Context, arg UpdateDeviceLastUsedParams) (Device, error) {
row := q.db.QueryRowContext(ctx, updateDeviceLastUsed, arg.IpAddress, arg.ID)
var i Device
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.ProfileID,
&i.CredentialID,
&i.Name,
&i.DeviceType,
&i.OsName,
&i.OsVersion,
&i.BrowserName,
&i.BrowserVersion,
&i.LastUsedAt,
&i.IsTrusted,
&i.IsCurrent,
&i.Fingerprint,
&i.UserAgent,
&i.IpAddress,
)
return i, err
}
const updateProfile = `-- name: UpdateProfile :one
UPDATE profiles
SET

1
x/account/controller.go Normal file
View File

@@ -0,0 +1 @@
package account

9
x/account/model.go Normal file
View File

@@ -0,0 +1,9 @@
package account
import "github.com/onsonr/motr/internal/models"
type Account = models.Account
type AccountModel interface{}
type AccountViewModel struct{}

0
x/account/query.sql Normal file
View File

24
x/account/schema.sql Normal file
View File

@@ -0,0 +1,24 @@
-- 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,
chain_id TEXT NOT NULL,
controller TEXT NOT NULL,
is_subsidiary BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_subsidiary IN (0,1)),
is_validator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_validator IN (0,1)),
is_delegator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegator IN (0,1)),
is_accountable BOOLEAN NOT NULL DEFAULT TRUE CHECK(is_accountable IN (0,1))
);
CREATE INDEX idx_accounts_address ON accounts(address);
CREATE INDEX idx_accounts_chain_id ON accounts(chain_id);
CREATE INDEX idx_accounts_deleted_at ON accounts(deleted_at);

11
x/account/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "account"
out: "orm.gen.go"

11
x/account/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package account
func View(a *AccountViewModel) templ.Component {
return initialAccountView(a)
}
templ initialAccountView(a *AccountViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/account/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// 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 View(a *AccountViewModel) templ.Component {
return initialAccountView(a)
}
func initialAccountView(a *AccountViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/asset/controller.go Normal file
View File

@@ -0,0 +1 @@
package asset

9
x/asset/model.go Normal file
View File

@@ -0,0 +1,9 @@
package asset
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

0
x/asset/query.sql Normal file
View File

22
x/asset/schema.sql Normal file
View File

@@ -0,0 +1,22 @@
-- Assets represent tokens and coins
CREATE TABLE assets (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
decimals INTEGER NOT NULL CHECK(decimals >= 0),
chain_id TEXT NOT NULL,
channel TEXT NOT NULL,
asset_type TEXT NOT NULL,
coingecko_id TEXT,
UNIQUE(chain_id, symbol)
);
CREATE INDEX idx_assets_symbol ON assets(symbol);
CREATE INDEX idx_assets_chain_id ON assets(chain_id);
CREATE INDEX idx_assets_deleted_at ON assets(deleted_at);

11
x/asset/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "asset"
out: "orm.gen.go"

11
x/asset/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/asset/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/balance/controller.go Normal file
View File

@@ -0,0 +1 @@
package balance

9
x/balance/model.go Normal file
View File

@@ -0,0 +1,9 @@
package balance
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

48
x/balance/query.sql Normal file
View File

@@ -0,0 +1,48 @@
-- Balance table methods
-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetBalanceByID :one
SELECT * FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBalanceByAccountAndAsset :one
SELECT * FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListBalancesByAccount :many
SELECT * FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id;
-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

23
x/balance/schema.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Balances track asset holdings for accounts
CREATE TABLE balances (
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,
asset_id TEXT NOT NULL,
amount TEXT NOT NULL, -- Stored as string to handle large decimal numbers precisely
last_updated_height INTEGER NOT NULL DEFAULT 0,
is_delegated BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegated IN (0,1)),
is_staked BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_staked IN (0,1)),
is_vesting BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_vesting IN (0,1)),
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (asset_id) REFERENCES assets(id),
UNIQUE(account_id, asset_id)
);
CREATE INDEX idx_balances_account_id ON balances(account_id);
CREATE INDEX idx_balances_asset_id ON balances(asset_id);
CREATE INDEX idx_balances_deleted_at ON balances(deleted_at);

11
x/balance/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "balance"
out: "orm.gen.go"

11
x/balance/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/balance/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1 @@
package balance

9
x/credential/model.go Normal file
View File

@@ -0,0 +1,9 @@
package balance
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

48
x/credential/query.sql Normal file
View File

@@ -0,0 +1,48 @@
-- Balance table methods
-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetBalanceByID :one
SELECT * FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBalanceByAccountAndAsset :one
SELECT * FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListBalancesByAccount :many
SELECT * FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id;
-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

23
x/credential/schema.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Balances track asset holdings for accounts
CREATE TABLE balances (
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,
asset_id TEXT NOT NULL,
amount TEXT NOT NULL, -- Stored as string to handle large decimal numbers precisely
last_updated_height INTEGER NOT NULL DEFAULT 0,
is_delegated BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegated IN (0,1)),
is_staked BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_staked IN (0,1)),
is_vesting BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_vesting IN (0,1)),
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (asset_id) REFERENCES assets(id),
UNIQUE(account_id, asset_id)
);
CREATE INDEX idx_balances_account_id ON balances(account_id);
CREATE INDEX idx_balances_asset_id ON balances(asset_id);
CREATE INDEX idx_balances_deleted_at ON balances(deleted_at);

11
x/credential/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "credential"
out: "orm.gen.go"

11
x/credential/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/device/controller.go Normal file
View File

@@ -0,0 +1 @@
package balance

9
x/device/model.go Normal file
View File

@@ -0,0 +1,9 @@
package balance
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

48
x/device/query.sql Normal file
View File

@@ -0,0 +1,48 @@
-- Balance table methods
-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetBalanceByID :one
SELECT * FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBalanceByAccountAndAsset :one
SELECT * FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListBalancesByAccount :many
SELECT * FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id;
-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

23
x/device/schema.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Balances track asset holdings for accounts
CREATE TABLE balances (
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,
asset_id TEXT NOT NULL,
amount TEXT NOT NULL, -- Stored as string to handle large decimal numbers precisely
last_updated_height INTEGER NOT NULL DEFAULT 0,
is_delegated BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegated IN (0,1)),
is_staked BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_staked IN (0,1)),
is_vesting BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_vesting IN (0,1)),
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (asset_id) REFERENCES assets(id),
UNIQUE(account_id, asset_id)
);
CREATE INDEX idx_balances_account_id ON balances(account_id);
CREATE INDEX idx_balances_asset_id ON balances(asset_id);
CREATE INDEX idx_balances_deleted_at ON balances(deleted_at);

11
x/device/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "device"
out: "orm.gen.go"

11
x/device/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/device/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/profile/controller.go Normal file
View File

@@ -0,0 +1 @@
package balance

9
x/profile/model.go Normal file
View File

@@ -0,0 +1,9 @@
package balance
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

48
x/profile/query.sql Normal file
View File

@@ -0,0 +1,48 @@
-- Balance table methods
-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetBalanceByID :one
SELECT * FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBalanceByAccountAndAsset :one
SELECT * FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListBalancesByAccount :many
SELECT * FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id;
-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

23
x/profile/schema.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Balances track asset holdings for accounts
CREATE TABLE balances (
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,
asset_id TEXT NOT NULL,
amount TEXT NOT NULL, -- Stored as string to handle large decimal numbers precisely
last_updated_height INTEGER NOT NULL DEFAULT 0,
is_delegated BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegated IN (0,1)),
is_staked BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_staked IN (0,1)),
is_vesting BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_vesting IN (0,1)),
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (asset_id) REFERENCES assets(id),
UNIQUE(account_id, asset_id)
);
CREATE INDEX idx_balances_account_id ON balances(account_id);
CREATE INDEX idx_balances_asset_id ON balances(asset_id);
CREATE INDEX idx_balances_deleted_at ON balances(deleted_at);

11
x/profile/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "profile"
out: "orm.gen.go"

11
x/profile/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/profile/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/routes.go Normal file
View File

@@ -0,0 +1 @@
package x

1
x/session/controller.go Normal file
View File

@@ -0,0 +1 @@
package balance

9
x/session/model.go Normal file
View File

@@ -0,0 +1,9 @@
package balance
import "github.com/onsonr/motr/internal/models"
type Asset = models.Asset
type AssetModel interface{}
type AssetViewModel struct{}

48
x/session/query.sql Normal file
View File

@@ -0,0 +1,48 @@
-- Balance table methods
-- name: CreateBalance :one
INSERT INTO balances (
id,
account_id,
asset_id,
amount,
last_updated_height,
is_delegated,
is_staked,
is_vesting
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetBalanceByID :one
SELECT * FROM balances
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBalanceByAccountAndAsset :one
SELECT * FROM balances
WHERE account_id = ? AND asset_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListBalancesByAccount :many
SELECT * FROM balances
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY asset_id;
-- name: UpdateBalance :one
UPDATE balances
SET
amount = ?,
last_updated_height = ?,
is_delegated = ?,
is_staked = ?,
is_vesting = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBalance :exec
UPDATE balances
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

23
x/session/schema.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Balances track asset holdings for accounts
CREATE TABLE balances (
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,
asset_id TEXT NOT NULL,
amount TEXT NOT NULL, -- Stored as string to handle large decimal numbers precisely
last_updated_height INTEGER NOT NULL DEFAULT 0,
is_delegated BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegated IN (0,1)),
is_staked BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_staked IN (0,1)),
is_vesting BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_vesting IN (0,1)),
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (asset_id) REFERENCES assets(id),
UNIQUE(account_id, asset_id)
);
CREATE INDEX idx_balances_account_id ON balances(account_id);
CREATE INDEX idx_balances_asset_id ON balances(asset_id);
CREATE INDEX idx_balances_deleted_at ON balances(deleted_at);

11
x/session/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "session"
out: "orm.gen.go"

11
x/session/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package asset
func View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
templ initialAssetView(a *AssetViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/session/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *AssetViewModel) templ.Component {
return initialAssetView(a)
}
func initialAssetView(a *AssetViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

1
x/vault/controller.go Normal file
View File

@@ -0,0 +1 @@
package vault

9
x/vault/model.go Normal file
View File

@@ -0,0 +1,9 @@
package vault
import "github.com/onsonr/motr/internal/models"
type Vault = models.Vault
type VaultModel interface{}
type VaultViewModel struct{}

13
x/vault/query.sql Normal file
View File

@@ -0,0 +1,13 @@
-- name: GetVaultConfigByCID :one
SELECT * FROM vaults
WHERE cid = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = ?
AND deleted_at IS NULL
LIMIT 1;

18
x/vault/schema.sql Normal file
View File

@@ -0,0 +1,18 @@
-- 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,
session_id TEXT NOT NULL,
redirect_uri TEXT NOT NULL
);
CREATE INDEX idx_vaults_handle ON vaults(handle);
CREATE INDEX idx_vaults_session_id ON vaults(session_id);
CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at);

11
x/vault/sqlc.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "query.sql"
schema: "schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "vault"
out: "orm.gen.go"

11
x/vault/view.templ Normal file
View File

@@ -0,0 +1,11 @@
package vault
func View(a *VaultViewModel) templ.Component {
return initialVaultView(a)
}
templ initialVaultView(a *VaultViewModel) {
<div>
<h1>Account</h1>
</div>
}

44
x/vault/view_templ.go Normal file
View File

@@ -0,0 +1,44 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.857
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 View(a *VaultViewModel) templ.Component {
return initialVaultView(a)
}
func initialVaultView(a *VaultViewModel) 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><h1>Account</h1></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate