mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 18:01:39 +00:00
feature/simplify ucan mpc did (#1195)
* feat: enable DID auth middleware * feat: implement passkey creation flow * feat: persist user address in cookie and retrieve user profile using address cookie * feat: implement human verification challenge during session initialization * refactor: remove unnecessary random number generation in profile creation * refactor: rename credential validation handler and update related routes * feat: improve profile validation and user experience * feat: add page rendering for profile and passkey creation * refactor: remove unused register handler and update routes * refactor: remove unused imports and simplify credential validation * fix: Correct insecure gRPC client connection * refactor: rename models files for better organization * refactor: refactor grpc client creation and management * refactor: refactor common clients package * <no value> * feat: add CapAccount, CapInterchain, CapVault enums * feat: add ChainId to ResAccount and ResInterchain * feat: add asset code to resource account enumeration * refactor: rename services package to providers * feat: implement gateway database interactions * refactor: move gateway repository to internal/gateway * refactor: Migrate database provider to use sqlx * refactor: Rename Vaults to VaultProvider in HTTPContext struct * refactor: Migrate from GORM to sqlc Queries in database context methods * refactor: Replace GORM with standard SQL and simplify database initialization * refactor: Migrate session management from GORM to sqlc with type conversion * refactor: Update import paths and model references in context package * fix: Resolve session type conversion and middleware issues * refactor: Migrate database from GORM to sqlx * refactor: Move models to pkg/common, improve code structure * refactor: move repository package to internal directory * refactor: move gateway internal packages to context directory * refactor: migrate database provider to use sqlx queries * feat: add session ID to HTTP context and use it to load session data * feat: implement vault creation API endpoint * feat: add DIDKey generation from PubKey * refactor: remove unused DIDAuth components * refactor: move DID auth controller to vault context * chore: remove unused DIDAuth package * refactor: improve clarity of enclave refresh function * feat: implement nonce-based key encryption for improved security * feat: Add Export and Import methods with comprehensive tests for Enclave * fix: Validate AES key length in keyshare encryption and decryption * fix: Resolve key length validation by hashing input keys * refactor: Update keyshare import to use protocol decoding * feat: Refactor enclave encryption to support full enclave export/import * refactor: Simplify Enclave interface methods by removing role parameter * refactor: remove unnecessary serialization from enclave interface * refactor: rename models package in gateway context * refactor: rename keystore vault constants * refactor: remove context parameter from Resolver methods * feat: add CurrentBlock context function and update related components * refactor: rename resolver.go to resolvers.go * feat: Add SQLite random() generation for session and profile initialization * refactor: Update SQL queries to use SQLite-style parameter placeholders * refactor: Replace '?' placeholders with '$n' PostgreSQL parameter syntax * <no value> * refactor: refactor gateway to use middleware for database interactions and improve modularity * feat: implement gateway for Sonr highway * refactor: Remove unused gateway context and refactor cookie/header handling * refactor: improve server initialization and middleware handling * feat: implement human verification for profile creation * feat: implement session management middleware * refactor: refactor common models and config to internal package * refactor: move env config to internal/config * refactor: move database-related code to directory * refactor: move IPFS client to common package and improve code structure * refactor: move querier to common package and rename to chain_query * refactor: move webworker model to internal/models * feat: add initial view template for Sonr.ID * docs(concepts): Add documentation for cosmos-proto * docs: move IBC transfer documentation to tools section * refactor: rename initpkl.go to pkl_init.go for better naming consistency * docs(theme): update dark mode toggle icons * refactor: update sqlite3 driver to ncruces/go-sqlite3 * feat: add Vault model and database interactions * refactor: Improve SQLite schema with better constraints and indexes * chore: update project dependencies * fix: use grpc.WithInsecure() for gRPC connection * config: set localhost as default Sonr gRPC URL * refactor: improve gateway middleware and refactor server initialization * refactor: Remove foreign key pragma from schema SQL * refactor: Remove foreign key constraints from database schema * refactor: Convert primary key columns from INTEGER to TEXT * refactor: Remove unnecessary redirect in error handling
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/ncruces/go-sqlite3/driver"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
config "github.com/onsonr/sonr/internal/config/hway"
|
||||
"github.com/onsonr/sonr/internal/database/sink"
|
||||
)
|
||||
|
||||
// NewDB initializes and returns a configured database connection
|
||||
func NewDB(env config.Hway) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create tables
|
||||
if _, err := db.ExecContext(context.Background(), sink.SchemaSQL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
ID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
Number int64
|
||||
Sequence int64
|
||||
Address string
|
||||
PublicKey string
|
||||
ChainID string
|
||||
Controller string
|
||||
IsSubsidiary bool
|
||||
IsValidator bool
|
||||
IsDelegator bool
|
||||
IsAccountable bool
|
||||
}
|
||||
|
||||
type Asset struct {
|
||||
ID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
Name string
|
||||
Symbol string
|
||||
Decimals int64
|
||||
ChainID string
|
||||
Channel string
|
||||
AssetType string
|
||||
CoingeckoID sql.NullString
|
||||
}
|
||||
|
||||
type Credential struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
Handle string
|
||||
CredentialID string
|
||||
AuthenticatorAttachment string
|
||||
Origin string
|
||||
Type string
|
||||
Transports string
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
Address string
|
||||
Handle string
|
||||
Origin string
|
||||
Name string
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
ID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
BrowserName string
|
||||
BrowserVersion string
|
||||
ClientIpaddr string
|
||||
Platform string
|
||||
IsDesktop bool
|
||||
IsMobile bool
|
||||
IsTablet bool
|
||||
IsTv bool
|
||||
IsBot bool
|
||||
Challenge string
|
||||
IsHumanFirst bool
|
||||
IsHumanLast bool
|
||||
ProfileID int64
|
||||
}
|
||||
|
||||
type Vault struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt sql.NullTime
|
||||
Handle string
|
||||
Origin string
|
||||
Address string
|
||||
Cid string
|
||||
Config string
|
||||
SessionID string
|
||||
RedirectUri string
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: query.sql
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const checkHandleExists = `-- name: CheckHandleExists :one
|
||||
SELECT COUNT(*) > 0 as handle_exists FROM profiles
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
func (q *Queries) CheckHandleExists(ctx context.Context, handle string) (bool, error) {
|
||||
row := q.db.QueryRowContext(ctx, checkHandleExists, handle)
|
||||
var handle_exists bool
|
||||
err := row.Scan(&handle_exists)
|
||||
return handle_exists, err
|
||||
}
|
||||
|
||||
const createSession = `-- name: CreateSession :one
|
||||
INSERT INTO sessions (
|
||||
id,
|
||||
browser_name,
|
||||
browser_version,
|
||||
client_ipaddr,
|
||||
platform,
|
||||
is_desktop,
|
||||
is_mobile,
|
||||
is_tablet,
|
||||
is_tv,
|
||||
is_bot,
|
||||
challenge,
|
||||
is_human_first,
|
||||
is_human_last,
|
||||
profile_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
|
||||
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
|
||||
`
|
||||
|
||||
type CreateSessionParams struct {
|
||||
ID string
|
||||
BrowserName string
|
||||
BrowserVersion string
|
||||
ClientIpaddr string
|
||||
Platform string
|
||||
IsDesktop bool
|
||||
IsMobile bool
|
||||
IsTablet bool
|
||||
IsTv bool
|
||||
IsBot bool
|
||||
Challenge string
|
||||
IsHumanFirst bool
|
||||
IsHumanLast bool
|
||||
ProfileID int64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
|
||||
row := q.db.QueryRowContext(ctx, createSession,
|
||||
arg.ID,
|
||||
arg.BrowserName,
|
||||
arg.BrowserVersion,
|
||||
arg.ClientIpaddr,
|
||||
arg.Platform,
|
||||
arg.IsDesktop,
|
||||
arg.IsMobile,
|
||||
arg.IsTablet,
|
||||
arg.IsTv,
|
||||
arg.IsBot,
|
||||
arg.Challenge,
|
||||
arg.IsHumanFirst,
|
||||
arg.IsHumanLast,
|
||||
arg.ProfileID,
|
||||
)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.BrowserName,
|
||||
&i.BrowserVersion,
|
||||
&i.ClientIpaddr,
|
||||
&i.Platform,
|
||||
&i.IsDesktop,
|
||||
&i.IsMobile,
|
||||
&i.IsTablet,
|
||||
&i.IsTv,
|
||||
&i.IsBot,
|
||||
&i.Challenge,
|
||||
&i.IsHumanFirst,
|
||||
&i.IsHumanLast,
|
||||
&i.ProfileID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getChallengeBySessionID = `-- name: GetChallengeBySessionID :one
|
||||
SELECT challenge FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetChallengeBySessionID(ctx context.Context, id string) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getChallengeBySessionID, id)
|
||||
var challenge string
|
||||
err := row.Scan(&challenge)
|
||||
return challenge, err
|
||||
}
|
||||
|
||||
const getCredentialByID = `-- name: GetCredentialByID :one
|
||||
SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials
|
||||
WHERE credential_id = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCredentialByID(ctx context.Context, credentialID string) (Credential, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCredentialByID, 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 getCredentialsByHandle = `-- name: GetCredentialsByHandle :many
|
||||
SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
func (q *Queries) GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCredentialsByHandle, handle)
|
||||
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 getHumanVerificationNumbers = `-- name: GetHumanVerificationNumbers :one
|
||||
SELECT is_human_first, is_human_last FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetHumanVerificationNumbersRow struct {
|
||||
IsHumanFirst bool
|
||||
IsHumanLast bool
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanVerificationNumbers(ctx context.Context, id string) (GetHumanVerificationNumbersRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getHumanVerificationNumbers, id)
|
||||
var i GetHumanVerificationNumbersRow
|
||||
err := row.Scan(&i.IsHumanFirst, &i.IsHumanLast)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getProfileByAddress = `-- name: GetProfileByAddress :one
|
||||
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
|
||||
WHERE address = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetProfileByAddress(ctx context.Context, address string) (Profile, error) {
|
||||
row := q.db.QueryRowContext(ctx, getProfileByAddress, address)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Address,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Name,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getProfileByHandle = `-- name: GetProfileByHandle :one
|
||||
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetProfileByHandle(ctx context.Context, handle string) (Profile, error) {
|
||||
row := q.db.QueryRowContext(ctx, getProfileByHandle, handle)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Address,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Name,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getProfileByID = `-- name: GetProfileByID :one
|
||||
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetProfileByID(ctx context.Context, id int64) (Profile, error) {
|
||||
row := q.db.QueryRowContext(ctx, getProfileByID, id)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Address,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Name,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSessionByClientIP = `-- name: GetSessionByClientIP :one
|
||||
SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions
|
||||
WHERE client_ipaddr = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error) {
|
||||
row := q.db.QueryRowContext(ctx, getSessionByClientIP, clientIpaddr)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.BrowserName,
|
||||
&i.BrowserVersion,
|
||||
&i.ClientIpaddr,
|
||||
&i.Platform,
|
||||
&i.IsDesktop,
|
||||
&i.IsMobile,
|
||||
&i.IsTablet,
|
||||
&i.IsTv,
|
||||
&i.IsBot,
|
||||
&i.Challenge,
|
||||
&i.IsHumanFirst,
|
||||
&i.IsHumanLast,
|
||||
&i.ProfileID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSessionByID = `-- name: GetSessionByID :one
|
||||
SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error) {
|
||||
row := q.db.QueryRowContext(ctx, getSessionByID, id)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.BrowserName,
|
||||
&i.BrowserVersion,
|
||||
&i.ClientIpaddr,
|
||||
&i.Platform,
|
||||
&i.IsDesktop,
|
||||
&i.IsMobile,
|
||||
&i.IsTablet,
|
||||
&i.IsTv,
|
||||
&i.IsBot,
|
||||
&i.Challenge,
|
||||
&i.IsHumanFirst,
|
||||
&i.IsHumanLast,
|
||||
&i.ProfileID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
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 = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error) {
|
||||
row := q.db.QueryRowContext(ctx, getVaultConfigByCID, cid)
|
||||
var i Vault
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Address,
|
||||
&i.Cid,
|
||||
&i.Config,
|
||||
&i.SessionID,
|
||||
&i.RedirectUri,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getVaultRedirectURIBySessionID = `-- name: GetVaultRedirectURIBySessionID :one
|
||||
SELECT redirect_uri FROM vaults
|
||||
WHERE session_id = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getVaultRedirectURIBySessionID, sessionID)
|
||||
var redirect_uri string
|
||||
err := row.Scan(&redirect_uri)
|
||||
return redirect_uri, err
|
||||
}
|
||||
|
||||
const insertCredential = `-- name: InsertCredential :one
|
||||
INSERT INTO credentials (
|
||||
handle,
|
||||
credential_id,
|
||||
origin,
|
||||
type,
|
||||
transports
|
||||
) VALUES (?, ?, ?, ?, ?)
|
||||
RETURNING id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports
|
||||
`
|
||||
|
||||
type InsertCredentialParams struct {
|
||||
Handle string
|
||||
CredentialID string
|
||||
Origin string
|
||||
Type string
|
||||
Transports string
|
||||
}
|
||||
|
||||
func (q *Queries) InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertCredential,
|
||||
arg.Handle,
|
||||
arg.CredentialID,
|
||||
arg.Origin,
|
||||
arg.Type,
|
||||
arg.Transports,
|
||||
)
|
||||
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 insertProfile = `-- name: InsertProfile :one
|
||||
INSERT INTO profiles (
|
||||
address,
|
||||
handle,
|
||||
origin,
|
||||
name
|
||||
) VALUES (?, ?, ?, ?)
|
||||
RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name
|
||||
`
|
||||
|
||||
type InsertProfileParams struct {
|
||||
Address string
|
||||
Handle string
|
||||
Origin string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (q *Queries) InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertProfile,
|
||||
arg.Address,
|
||||
arg.Handle,
|
||||
arg.Origin,
|
||||
arg.Name,
|
||||
)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Address,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Name,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const softDeleteCredential = `-- name: SoftDeleteCredential :exec
|
||||
UPDATE credentials
|
||||
SET deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE credential_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) SoftDeleteCredential(ctx context.Context, credentialID string) error {
|
||||
_, err := q.db.ExecContext(ctx, softDeleteCredential, credentialID)
|
||||
return err
|
||||
}
|
||||
|
||||
const softDeleteProfile = `-- name: SoftDeleteProfile :exec
|
||||
UPDATE profiles
|
||||
SET deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE address = ?
|
||||
`
|
||||
|
||||
func (q *Queries) SoftDeleteProfile(ctx context.Context, address string) error {
|
||||
_, err := q.db.ExecContext(ctx, softDeleteProfile, address)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateProfile = `-- name: UpdateProfile :one
|
||||
UPDATE profiles
|
||||
SET
|
||||
name = ?,
|
||||
handle = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE address = ?
|
||||
AND deleted_at IS NULL
|
||||
RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name
|
||||
`
|
||||
|
||||
type UpdateProfileParams struct {
|
||||
Name string
|
||||
Handle string
|
||||
Address string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateProfile, arg.Name, arg.Handle, arg.Address)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Address,
|
||||
&i.Handle,
|
||||
&i.Origin,
|
||||
&i.Name,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateSessionHumanVerification = `-- name: UpdateSessionHumanVerification :one
|
||||
UPDATE sessions
|
||||
SET
|
||||
is_human_first = ?,
|
||||
is_human_last = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
|
||||
`
|
||||
|
||||
type UpdateSessionHumanVerificationParams struct {
|
||||
IsHumanFirst bool
|
||||
IsHumanLast bool
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSessionHumanVerification(ctx context.Context, arg UpdateSessionHumanVerificationParams) (Session, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateSessionHumanVerification, arg.IsHumanFirst, arg.IsHumanLast, arg.ID)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.BrowserName,
|
||||
&i.BrowserVersion,
|
||||
&i.ClientIpaddr,
|
||||
&i.Platform,
|
||||
&i.IsDesktop,
|
||||
&i.IsMobile,
|
||||
&i.IsTablet,
|
||||
&i.IsTv,
|
||||
&i.IsBot,
|
||||
&i.Challenge,
|
||||
&i.IsHumanFirst,
|
||||
&i.IsHumanLast,
|
||||
&i.ProfileID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateSessionWithProfileID = `-- name: UpdateSessionWithProfileID :one
|
||||
UPDATE sessions
|
||||
SET
|
||||
profile_id = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
|
||||
`
|
||||
|
||||
type UpdateSessionWithProfileIDParams struct {
|
||||
ProfileID int64
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateSessionWithProfileID, arg.ProfileID, arg.ID)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.BrowserName,
|
||||
&i.BrowserVersion,
|
||||
&i.ClientIpaddr,
|
||||
&i.Platform,
|
||||
&i.IsDesktop,
|
||||
&i.IsMobile,
|
||||
&i.IsTablet,
|
||||
&i.IsTv,
|
||||
&i.IsBot,
|
||||
&i.Challenge,
|
||||
&i.IsHumanFirst,
|
||||
&i.IsHumanLast,
|
||||
&i.ProfileID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
ctx "github.com/onsonr/sonr/internal/context"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/medama-io/go-useragent"
|
||||
"github.com/onsonr/sonr/internal/database/repository"
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
func BaseSessionCreateParams(e echo.Context) repository.CreateSessionParams {
|
||||
// f := rand.Intn(5) + 1
|
||||
// l := rand.Intn(4) + 1
|
||||
challenge, _ := protocol.CreateChallenge()
|
||||
id := getOrCreateSessionID(e)
|
||||
ua := useragent.NewParser()
|
||||
s := ua.Parse(e.Request().UserAgent())
|
||||
|
||||
return repository.CreateSessionParams{
|
||||
ID: id,
|
||||
BrowserName: s.GetBrowser(),
|
||||
BrowserVersion: s.GetMajorVersion(),
|
||||
ClientIpaddr: e.RealIP(),
|
||||
Platform: s.GetOS(),
|
||||
IsMobile: s.IsMobile(),
|
||||
IsTablet: s.IsTablet(),
|
||||
IsDesktop: s.IsDesktop(),
|
||||
IsBot: s.IsBot(),
|
||||
IsTv: s.IsTV(),
|
||||
// IsHumanFirst: int64(f),
|
||||
// IsHumanLast: int64(l),
|
||||
Challenge: challenge.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func getOrCreateSessionID(c echo.Context) string {
|
||||
if ok := ctx.CookieExists(c, ctx.SessionID); !ok {
|
||||
sessionID := ksuid.New().String()
|
||||
ctx.WriteCookie(c, ctx.SessionID, sessionID)
|
||||
return sessionID
|
||||
}
|
||||
|
||||
sessionID, err := ctx.ReadCookie(c, ctx.SessionID)
|
||||
if err != nil {
|
||||
sessionID = ksuid.New().String()
|
||||
ctx.WriteCookie(c, ctx.SessionID, sessionID)
|
||||
}
|
||||
return sessionID
|
||||
}
|
||||
|
||||
func boolToInt64(b bool) int64 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package sink
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed schema.sql
|
||||
var SchemaSQL string
|
||||
@@ -0,0 +1,138 @@
|
||||
-- name: InsertCredential :one
|
||||
INSERT INTO credentials (
|
||||
handle,
|
||||
credential_id,
|
||||
origin,
|
||||
type,
|
||||
transports
|
||||
) VALUES (?, ?, ?, ?, ?)
|
||||
RETURNING *;
|
||||
|
||||
-- name: InsertProfile :one
|
||||
INSERT INTO profiles (
|
||||
address,
|
||||
handle,
|
||||
origin,
|
||||
name
|
||||
) VALUES (?, ?, ?, ?)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetProfileByID :one
|
||||
SELECT * FROM profiles
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetProfileByAddress :one
|
||||
SELECT * FROM profiles
|
||||
WHERE address = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetChallengeBySessionID :one
|
||||
SELECT challenge FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetHumanVerificationNumbers :one
|
||||
SELECT is_human_first, is_human_last FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetSessionByID :one
|
||||
SELECT * FROM sessions
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetSessionByClientIP :one
|
||||
SELECT * FROM sessions
|
||||
WHERE client_ipaddr = ? AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: UpdateSessionHumanVerification :one
|
||||
UPDATE sessions
|
||||
SET
|
||||
is_human_first = ?,
|
||||
is_human_last = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateSessionWithProfileID :one
|
||||
UPDATE sessions
|
||||
SET
|
||||
profile_id = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
RETURNING *;
|
||||
|
||||
-- name: CheckHandleExists :one
|
||||
SELECT COUNT(*) > 0 as handle_exists FROM profiles
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
-- name: GetCredentialsByHandle :many
|
||||
SELECT * FROM credentials
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
-- name: GetCredentialByID :one
|
||||
SELECT * FROM credentials
|
||||
WHERE credential_id = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: SoftDeleteCredential :exec
|
||||
UPDATE credentials
|
||||
SET deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE credential_id = ?;
|
||||
|
||||
-- name: SoftDeleteProfile :exec
|
||||
UPDATE profiles
|
||||
SET deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE address = ?;
|
||||
|
||||
-- name: UpdateProfile :one
|
||||
UPDATE profiles
|
||||
SET
|
||||
name = ?,
|
||||
handle = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE address = ?
|
||||
AND deleted_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetProfileByHandle :one
|
||||
SELECT * FROM profiles
|
||||
WHERE handle = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetVaultConfigByCID :one
|
||||
SELECT * FROM vaults
|
||||
WHERE cid = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetVaultRedirectURIBySessionID :one
|
||||
SELECT redirect_uri FROM vaults
|
||||
WHERE session_id = ?
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: CreateSession :one
|
||||
INSERT INTO sessions (
|
||||
id,
|
||||
browser_name,
|
||||
browser_version,
|
||||
client_ipaddr,
|
||||
platform,
|
||||
is_desktop,
|
||||
is_mobile,
|
||||
is_tablet,
|
||||
is_tv,
|
||||
is_bot,
|
||||
challenge,
|
||||
is_human_first,
|
||||
is_human_last,
|
||||
profile_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,121 @@
|
||||
-- Profiles represent user identities
|
||||
CREATE TABLE profiles (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
address TEXT NOT NULL,
|
||||
handle TEXT NOT NULL UNIQUE,
|
||||
origin TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
UNIQUE(address, origin)
|
||||
);
|
||||
|
||||
-- Accounts represent blockchain accounts
|
||||
CREATE TABLE accounts (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
number INTEGER NOT NULL,
|
||||
sequence INTEGER NOT NULL DEFAULT 0,
|
||||
address TEXT NOT NULL UNIQUE,
|
||||
public_key TEXT NOT NULL CHECK(json_valid(public_key)),
|
||||
chain_id TEXT NOT NULL,
|
||||
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))
|
||||
);
|
||||
|
||||
-- Assets represent tokens and coins
|
||||
CREATE TABLE assets (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
name TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
decimals INTEGER NOT NULL CHECK(decimals >= 0),
|
||||
chain_id TEXT NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
asset_type TEXT NOT NULL,
|
||||
coingecko_id TEXT,
|
||||
UNIQUE(chain_id, symbol)
|
||||
);
|
||||
|
||||
-- Credentials store WebAuthn credentials
|
||||
CREATE TABLE credentials (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
handle TEXT NOT NULL,
|
||||
credential_id TEXT NOT NULL UNIQUE,
|
||||
authenticator_attachment TEXT NOT NULL,
|
||||
origin TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
transports TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Sessions track user authentication state
|
||||
CREATE TABLE sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
browser_name TEXT NOT NULL,
|
||||
browser_version TEXT NOT NULL,
|
||||
client_ipaddr TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
is_desktop BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_desktop IN (0,1)),
|
||||
is_mobile BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_mobile IN (0,1)),
|
||||
is_tablet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tablet IN (0,1)),
|
||||
is_tv BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tv IN (0,1)),
|
||||
is_bot BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_bot IN (0,1)),
|
||||
challenge TEXT NOT NULL,
|
||||
is_human_first BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_first IN (0,1)),
|
||||
is_human_last BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_last IN (0,1)),
|
||||
profile_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Vaults store encrypted data
|
||||
CREATE TABLE vaults (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
handle TEXT NOT NULL,
|
||||
origin TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
cid TEXT NOT NULL UNIQUE,
|
||||
config TEXT NOT NULL CHECK(json_valid(config)),
|
||||
session_id TEXT NOT NULL,
|
||||
redirect_uri TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes for common queries
|
||||
CREATE INDEX idx_profiles_handle ON profiles(handle);
|
||||
CREATE INDEX idx_profiles_address ON profiles(address);
|
||||
CREATE INDEX idx_profiles_deleted_at ON profiles(deleted_at);
|
||||
|
||||
CREATE INDEX idx_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);
|
||||
|
||||
CREATE INDEX idx_assets_symbol ON assets(symbol);
|
||||
CREATE INDEX idx_assets_chain_id ON assets(chain_id);
|
||||
CREATE INDEX idx_assets_deleted_at ON assets(deleted_at);
|
||||
|
||||
CREATE INDEX idx_credentials_handle ON credentials(handle);
|
||||
CREATE INDEX idx_credentials_origin ON credentials(origin);
|
||||
CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at);
|
||||
|
||||
CREATE INDEX idx_sessions_profile_id ON sessions(profile_id);
|
||||
CREATE INDEX idx_sessions_client_ipaddr ON sessions(client_ipaddr);
|
||||
CREATE INDEX idx_sessions_deleted_at ON sessions(deleted_at);
|
||||
|
||||
CREATE INDEX idx_vaults_handle ON vaults(handle);
|
||||
CREATE INDEX idx_vaults_session_id ON vaults(session_id);
|
||||
CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at);
|
||||
@@ -0,0 +1,9 @@
|
||||
version: "2"
|
||||
sql:
|
||||
- engine: "sqlite"
|
||||
queries: "./sink/query.sql"
|
||||
schema: "./sink/schema.sql"
|
||||
gen:
|
||||
go:
|
||||
package: "repository"
|
||||
out: "repository"
|
||||
Reference in New Issue
Block a user