diff --git a/.taskfiles/Default.yml b/.taskfiles/Default.yml index 2397b5e..c809a58 100644 --- a/.taskfiles/Default.yml +++ b/.taskfiles/Default.yml @@ -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}}" diff --git a/.taskfiles/Generate.yml b/.taskfiles/Generate.yml new file mode 100644 index 0000000..e0ac713 --- /dev/null +++ b/.taskfiles/Generate.yml @@ -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 diff --git a/internal/models/models.go b/internal/models/models.go index a20cae1..9456e7c 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -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"` diff --git a/internal/models/querier.go b/internal/models/querier.go index 1e71449..65f0de4 100644 --- a/internal/models/querier.go +++ b/internal/models/querier.go @@ -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) diff --git a/internal/models/query.sql.go b/internal/models/query.sql.go index 2c0170e..08de036 100644 --- a/internal/models/query.sql.go +++ b/internal/models/query.sql.go @@ -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 diff --git a/x/account/controller.go b/x/account/controller.go new file mode 100644 index 0000000..0bc7635 --- /dev/null +++ b/x/account/controller.go @@ -0,0 +1 @@ +package account diff --git a/x/account/model.go b/x/account/model.go new file mode 100644 index 0000000..95eeb7b --- /dev/null +++ b/x/account/model.go @@ -0,0 +1,9 @@ +package account + +import "github.com/onsonr/motr/internal/models" + +type Account = models.Account + +type AccountModel interface{} + +type AccountViewModel struct{} diff --git a/x/account/query.sql b/x/account/query.sql new file mode 100644 index 0000000..e69de29 diff --git a/x/account/schema.sql b/x/account/schema.sql new file mode 100644 index 0000000..a921d9f --- /dev/null +++ b/x/account/schema.sql @@ -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); + + diff --git a/x/account/sqlc.yaml b/x/account/sqlc.yaml new file mode 100644 index 0000000..e544a5f --- /dev/null +++ b/x/account/sqlc.yaml @@ -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" diff --git a/x/account/view.templ b/x/account/view.templ new file mode 100644 index 0000000..c1a18b0 --- /dev/null +++ b/x/account/view.templ @@ -0,0 +1,11 @@ +package account + +func View(a *AccountViewModel) templ.Component { + return initialAccountView(a) +} + +templ initialAccountView(a *AccountViewModel) { +
+

Account

+
+} diff --git a/x/account/view_templ.go b/x/account/view_templ.go new file mode 100644 index 0000000..665ba9d --- /dev/null +++ b/x/account/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/asset/controller.go b/x/asset/controller.go new file mode 100644 index 0000000..29bdbf9 --- /dev/null +++ b/x/asset/controller.go @@ -0,0 +1 @@ +package asset diff --git a/x/asset/model.go b/x/asset/model.go new file mode 100644 index 0000000..7fd12a4 --- /dev/null +++ b/x/asset/model.go @@ -0,0 +1,9 @@ +package asset + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/asset/query.sql b/x/asset/query.sql new file mode 100644 index 0000000..e69de29 diff --git a/x/asset/schema.sql b/x/asset/schema.sql new file mode 100644 index 0000000..9d86fae --- /dev/null +++ b/x/asset/schema.sql @@ -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); + + diff --git a/x/asset/sqlc.yaml b/x/asset/sqlc.yaml new file mode 100644 index 0000000..ab204ec --- /dev/null +++ b/x/asset/sqlc.yaml @@ -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" diff --git a/x/asset/view.templ b/x/asset/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/asset/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/asset/view_templ.go b/x/asset/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/asset/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/balance/controller.go b/x/balance/controller.go new file mode 100644 index 0000000..fafc330 --- /dev/null +++ b/x/balance/controller.go @@ -0,0 +1 @@ +package balance diff --git a/x/balance/model.go b/x/balance/model.go new file mode 100644 index 0000000..0681d88 --- /dev/null +++ b/x/balance/model.go @@ -0,0 +1,9 @@ +package balance + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/balance/query.sql b/x/balance/query.sql new file mode 100644 index 0000000..21f970b --- /dev/null +++ b/x/balance/query.sql @@ -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 = ?; + + diff --git a/x/balance/schema.sql b/x/balance/schema.sql new file mode 100644 index 0000000..13fc1da --- /dev/null +++ b/x/balance/schema.sql @@ -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); + + diff --git a/x/balance/sqlc.yaml b/x/balance/sqlc.yaml new file mode 100644 index 0000000..a3ad6ae --- /dev/null +++ b/x/balance/sqlc.yaml @@ -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" diff --git a/x/balance/view.templ b/x/balance/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/balance/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/balance/view_templ.go b/x/balance/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/balance/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/credential/controller.go b/x/credential/controller.go new file mode 100644 index 0000000..fafc330 --- /dev/null +++ b/x/credential/controller.go @@ -0,0 +1 @@ +package balance diff --git a/x/credential/model.go b/x/credential/model.go new file mode 100644 index 0000000..0681d88 --- /dev/null +++ b/x/credential/model.go @@ -0,0 +1,9 @@ +package balance + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/credential/query.sql b/x/credential/query.sql new file mode 100644 index 0000000..21f970b --- /dev/null +++ b/x/credential/query.sql @@ -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 = ?; + + diff --git a/x/credential/schema.sql b/x/credential/schema.sql new file mode 100644 index 0000000..13fc1da --- /dev/null +++ b/x/credential/schema.sql @@ -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); + + diff --git a/x/credential/sqlc.yaml b/x/credential/sqlc.yaml new file mode 100644 index 0000000..7271ed0 --- /dev/null +++ b/x/credential/sqlc.yaml @@ -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" diff --git a/x/credential/view.templ b/x/credential/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/credential/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/credential/view_templ.go b/x/credential/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/credential/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/device/controller.go b/x/device/controller.go new file mode 100644 index 0000000..fafc330 --- /dev/null +++ b/x/device/controller.go @@ -0,0 +1 @@ +package balance diff --git a/x/device/model.go b/x/device/model.go new file mode 100644 index 0000000..0681d88 --- /dev/null +++ b/x/device/model.go @@ -0,0 +1,9 @@ +package balance + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/device/query.sql b/x/device/query.sql new file mode 100644 index 0000000..21f970b --- /dev/null +++ b/x/device/query.sql @@ -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 = ?; + + diff --git a/x/device/schema.sql b/x/device/schema.sql new file mode 100644 index 0000000..13fc1da --- /dev/null +++ b/x/device/schema.sql @@ -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); + + diff --git a/x/device/sqlc.yaml b/x/device/sqlc.yaml new file mode 100644 index 0000000..4212bd1 --- /dev/null +++ b/x/device/sqlc.yaml @@ -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" diff --git a/x/device/view.templ b/x/device/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/device/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/device/view_templ.go b/x/device/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/device/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/profile/controller.go b/x/profile/controller.go new file mode 100644 index 0000000..fafc330 --- /dev/null +++ b/x/profile/controller.go @@ -0,0 +1 @@ +package balance diff --git a/x/profile/model.go b/x/profile/model.go new file mode 100644 index 0000000..0681d88 --- /dev/null +++ b/x/profile/model.go @@ -0,0 +1,9 @@ +package balance + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/profile/query.sql b/x/profile/query.sql new file mode 100644 index 0000000..21f970b --- /dev/null +++ b/x/profile/query.sql @@ -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 = ?; + + diff --git a/x/profile/schema.sql b/x/profile/schema.sql new file mode 100644 index 0000000..13fc1da --- /dev/null +++ b/x/profile/schema.sql @@ -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); + + diff --git a/x/profile/sqlc.yaml b/x/profile/sqlc.yaml new file mode 100644 index 0000000..21b6b73 --- /dev/null +++ b/x/profile/sqlc.yaml @@ -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" diff --git a/x/profile/view.templ b/x/profile/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/profile/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/profile/view_templ.go b/x/profile/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/profile/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/routes.go b/x/routes.go new file mode 100644 index 0000000..823aafd --- /dev/null +++ b/x/routes.go @@ -0,0 +1 @@ +package x diff --git a/x/session/controller.go b/x/session/controller.go new file mode 100644 index 0000000..fafc330 --- /dev/null +++ b/x/session/controller.go @@ -0,0 +1 @@ +package balance diff --git a/x/session/model.go b/x/session/model.go new file mode 100644 index 0000000..0681d88 --- /dev/null +++ b/x/session/model.go @@ -0,0 +1,9 @@ +package balance + +import "github.com/onsonr/motr/internal/models" + +type Asset = models.Asset + +type AssetModel interface{} + +type AssetViewModel struct{} diff --git a/x/session/query.sql b/x/session/query.sql new file mode 100644 index 0000000..21f970b --- /dev/null +++ b/x/session/query.sql @@ -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 = ?; + + diff --git a/x/session/schema.sql b/x/session/schema.sql new file mode 100644 index 0000000..13fc1da --- /dev/null +++ b/x/session/schema.sql @@ -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); + + diff --git a/x/session/sqlc.yaml b/x/session/sqlc.yaml new file mode 100644 index 0000000..1edd810 --- /dev/null +++ b/x/session/sqlc.yaml @@ -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" diff --git a/x/session/view.templ b/x/session/view.templ new file mode 100644 index 0000000..c74b0ac --- /dev/null +++ b/x/session/view.templ @@ -0,0 +1,11 @@ +package asset + +func View(a *AssetViewModel) templ.Component { + return initialAssetView(a) +} + +templ initialAssetView(a *AssetViewModel) { +
+

Account

+
+} diff --git a/x/session/view_templ.go b/x/session/view_templ.go new file mode 100644 index 0000000..fcc10a0 --- /dev/null +++ b/x/session/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/x/vault/controller.go b/x/vault/controller.go new file mode 100644 index 0000000..f7e34c8 --- /dev/null +++ b/x/vault/controller.go @@ -0,0 +1 @@ +package vault diff --git a/x/vault/model.go b/x/vault/model.go new file mode 100644 index 0000000..6b8a57d --- /dev/null +++ b/x/vault/model.go @@ -0,0 +1,9 @@ +package vault + +import "github.com/onsonr/motr/internal/models" + +type Vault = models.Vault + +type VaultModel interface{} + +type VaultViewModel struct{} diff --git a/x/vault/query.sql b/x/vault/query.sql new file mode 100644 index 0000000..204c457 --- /dev/null +++ b/x/vault/query.sql @@ -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; + diff --git a/x/vault/schema.sql b/x/vault/schema.sql new file mode 100644 index 0000000..e1f6b63 --- /dev/null +++ b/x/vault/schema.sql @@ -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); diff --git a/x/vault/sqlc.yaml b/x/vault/sqlc.yaml new file mode 100644 index 0000000..f12f3f2 --- /dev/null +++ b/x/vault/sqlc.yaml @@ -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" diff --git a/x/vault/view.templ b/x/vault/view.templ new file mode 100644 index 0000000..908d77d --- /dev/null +++ b/x/vault/view.templ @@ -0,0 +1,11 @@ +package vault + +func View(a *VaultViewModel) templ.Component { + return initialVaultView(a) +} + +templ initialVaultView(a *VaultViewModel) { +
+

Account

+
+} diff --git a/x/vault/view_templ.go b/x/vault/view_templ.go new file mode 100644 index 0000000..9664722 --- /dev/null +++ b/x/vault/view_templ.go @@ -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, "

Account

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate