mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 01:41:44 +00:00
feature/implement wss routes (#1196)
* feat(database): create schema for hway and motr * fix(gateway): correct naming inconsistencies in handlers * build: update schema file to be compatible with postgresql syntax * fix: update schema to be compatible with PostgreSQL syntax * chore: update query_hway.sql to follow sqlc syntax * ```text refactor: update query_hway.sql for PostgreSQL and sqlc ``` * feat: add vaults table to store encrypted data * refactor: Update vaults table schema for sqlc compatibility * chore(deps): Upgrade dependencies and add pgx/v5 * refactor(Makefile): move sqlc generate to internal/models * docs(foundations): remove outdated pages * chore(build): add Taskfile for build tasks * refactor(embed): move embed files to internal package * docs: add documentation for Cosmos SDK ORM
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
echomiddleware "github.com/labstack/echo/v4/middleware"
|
||||
config "github.com/onsonr/sonr/internal/config/hway"
|
||||
"github.com/onsonr/sonr/internal/database"
|
||||
"github.com/onsonr/sonr/internal/models/drivers/hwayorm"
|
||||
"github.com/onsonr/sonr/pkg/common"
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
"github.com/onsonr/sonr/pkg/gateway/routes"
|
||||
@@ -15,11 +15,8 @@ import (
|
||||
type Gateway = *echo.Echo
|
||||
|
||||
// New returns a new Gateway instance
|
||||
func New(env config.Hway, ipc common.IPFS) (Gateway, error) {
|
||||
db, err := database.NewDB(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func New(env config.Hway, ipc common.IPFS, dbq *hwayorm.Queries) (Gateway, error) {
|
||||
|
||||
e := echo.New()
|
||||
// Override default behaviors
|
||||
e.IPExtractor = echo.ExtractIPDirect()
|
||||
@@ -29,7 +26,7 @@ func New(env config.Hway, ipc common.IPFS) (Gateway, error) {
|
||||
e.Use(echoprometheus.NewMiddleware("hway"))
|
||||
e.Use(echomiddleware.Logger())
|
||||
e.Use(echomiddleware.Recover())
|
||||
e.Use(middleware.UseGateway(env, ipc, db))
|
||||
e.Use(middleware.UseGateway(env, ipc, dbq))
|
||||
routes.Register(e)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ package handlers
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
"github.com/onsonr/sonr/internal/nebula/input"
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
)
|
||||
|
||||
// ValidateProfileHandle finds the chosen handle and verifies it is unique
|
||||
func ValidateProfileHandle(c echo.Context) error {
|
||||
// CheckProfileHandle finds the chosen handle and verifies it is unique
|
||||
func CheckProfileHandle(c echo.Context) error {
|
||||
handle := c.FormValue("handle")
|
||||
if handle == "" {
|
||||
return middleware.Render(c, input.HandleError(handle, "Please enter a valid handle"))
|
||||
}
|
||||
//
|
||||
// if ok {
|
||||
// return middleware.Render(c, input.HandleError(handle))
|
||||
@@ -19,7 +22,7 @@ func ValidateProfileHandle(c echo.Context) error {
|
||||
}
|
||||
|
||||
// ValidateProfileHandle finds the chosen handle and verifies it is unique
|
||||
func ValidateIsHumanSum(c echo.Context) error {
|
||||
func CheckIsHumanSum(c echo.Context) error {
|
||||
// data := context.GetCreateProfileData(c)
|
||||
// value := c.FormValue("is_human")
|
||||
// intValue, err := strconv.Atoi(value)
|
||||
|
||||
@@ -5,12 +5,24 @@ import (
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
)
|
||||
|
||||
func RenderIndex(c echo.Context) error {
|
||||
func HandleIndex(c echo.Context) error {
|
||||
id := middleware.GetSessionID(c)
|
||||
if id == "" {
|
||||
return startNewSession(c)
|
||||
}
|
||||
return middleware.RenderInitial(c)
|
||||
}
|
||||
|
||||
func startNewSession(c echo.Context) error {
|
||||
// Initialize the session
|
||||
err := middleware.NewSession(c)
|
||||
if err != nil {
|
||||
return middleware.RenderError(c, err)
|
||||
}
|
||||
// Render the initial view
|
||||
return middleware.RenderInitial(c)
|
||||
}
|
||||
|
||||
func continueExistingSession(c echo.Context, id string) error {
|
||||
// Do some auth checks here
|
||||
return middleware.RenderInitial(c)
|
||||
}
|
||||
|
||||
@@ -3,21 +3,20 @@ package handlers
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
"github.com/onsonr/sonr/pkg/gateway/views"
|
||||
)
|
||||
|
||||
func RenderProfileCreate(c echo.Context) error {
|
||||
// numF, numL := middleware.GetHumanVerificationNumbers(c)
|
||||
params := types.CreateProfileParams{
|
||||
params := middleware.CreateProfileParams{
|
||||
FirstNumber: int(middleware.CurrentBlock(c)),
|
||||
LastNumber: int(middleware.CurrentBlock(c)),
|
||||
}
|
||||
return middleware.Render(c, views.RegisterProfileView(params))
|
||||
return middleware.Render(c, views.RegisterProfileView(params.FirstNumber, params.LastNumber))
|
||||
}
|
||||
|
||||
func RenderPasskeyCreate(c echo.Context) error {
|
||||
return middleware.Render(c, views.RegisterPasskeyView(types.CreatePasskeyParams{}))
|
||||
return middleware.Render(c, views.RegisterPasskeyView("", "", "", "", ""))
|
||||
}
|
||||
|
||||
func RenderVaultLoading(c echo.Context) error {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
"github.com/onsonr/sonr/pkg/gateway/middleware"
|
||||
)
|
||||
|
||||
@@ -16,7 +15,7 @@ func SubmitProfileHandle(c echo.Context) error {
|
||||
// SubmitPublicKeyCredential submits a public key credential
|
||||
func SubmitPublicKeyCredential(c echo.Context) error {
|
||||
credentialJSON := c.FormValue("credential")
|
||||
cred := &types.CredentialDescriptor{}
|
||||
cred := &middleware.CredentialDescriptor{}
|
||||
// Unmarshal the credential JSON
|
||||
if err := json.Unmarshal([]byte(credentialJSON), cred); err != nil {
|
||||
return middleware.RenderError(c, err)
|
||||
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/internal/database/repository"
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
"github.com/onsonr/sonr/internal/models/drivers/hwayorm"
|
||||
)
|
||||
|
||||
func ListCredentials(c echo.Context, handle string) ([]*types.CredentialDescriptor, error) {
|
||||
func ListCredentials(c echo.Context, handle string) ([]*CredentialDescriptor, error) {
|
||||
cc, ok := c.(*GatewayContext)
|
||||
if !ok {
|
||||
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found")
|
||||
@@ -17,10 +16,10 @@ func ListCredentials(c echo.Context, handle string) ([]*types.CredentialDescript
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.CredentialArrayToDescriptors(creds), nil
|
||||
return CredentialArrayToDescriptors(creds), nil
|
||||
}
|
||||
|
||||
func SubmitCredential(c echo.Context, cred *types.CredentialDescriptor) error {
|
||||
func SubmitCredential(c echo.Context, cred *CredentialDescriptor) error {
|
||||
origin := GetOrigin(c)
|
||||
handle := GetHandle(c)
|
||||
md := cred.ToModel(handle, origin)
|
||||
@@ -30,7 +29,7 @@ func SubmitCredential(c echo.Context, cred *types.CredentialDescriptor) error {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found")
|
||||
}
|
||||
|
||||
_, err := cc.dbq.InsertCredential(bgCtx(), repository.InsertCredentialParams{
|
||||
_, err := cc.dbq.InsertCredential(bgCtx(), hwayorm.InsertCredentialParams{
|
||||
Handle: handle,
|
||||
CredentialID: md.CredentialID,
|
||||
Origin: origin,
|
||||
@@ -42,3 +41,43 @@ func SubmitCredential(c echo.Context, cred *types.CredentialDescriptor) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define the credential structure matching our frontend data
|
||||
type CredentialDescriptor struct {
|
||||
ID string `json:"id"`
|
||||
RawID string `json:"rawId"`
|
||||
Type string `json:"type"`
|
||||
AuthenticatorAttachment string `json:"authenticatorAttachment"`
|
||||
Transports string `json:"transports"`
|
||||
ClientExtensionResults map[string]string `json:"clientExtensionResults"`
|
||||
Response struct {
|
||||
AttestationObject string `json:"attestationObject"`
|
||||
ClientDataJSON string `json:"clientDataJSON"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
func (c *CredentialDescriptor) ToModel(handle, origin string) *hwayorm.Credential {
|
||||
return &hwayorm.Credential{
|
||||
Handle: handle,
|
||||
Origin: origin,
|
||||
CredentialID: c.ID,
|
||||
Type: c.Type,
|
||||
Transports: c.Transports,
|
||||
AuthenticatorAttachment: c.AuthenticatorAttachment,
|
||||
}
|
||||
}
|
||||
|
||||
func CredentialArrayToDescriptors(credentials []hwayorm.Credential) []*CredentialDescriptor {
|
||||
var descriptors []*CredentialDescriptor
|
||||
for _, cred := range credentials {
|
||||
cd := &CredentialDescriptor{
|
||||
ID: cred.CredentialID,
|
||||
RawID: cred.CredentialID,
|
||||
Type: cred.Type,
|
||||
AuthenticatorAttachment: cred.AuthenticatorAttachment,
|
||||
Transports: cred.Transports,
|
||||
}
|
||||
descriptors = append(descriptors, cd)
|
||||
}
|
||||
return descriptors
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/medama-io/go-useragent"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/internal/config/hway"
|
||||
"github.com/onsonr/sonr/internal/database/repository"
|
||||
"github.com/onsonr/sonr/internal/models/drivers/hwayorm"
|
||||
"github.com/onsonr/sonr/pkg/common"
|
||||
)
|
||||
|
||||
@@ -15,21 +13,21 @@ type GatewayContext struct {
|
||||
echo.Context
|
||||
agent useragent.UserAgent
|
||||
id string
|
||||
dbq *repository.Queries
|
||||
dbq *hwayorm.Queries
|
||||
ipfsClient common.IPFS
|
||||
tokenStore common.IPFSTokenStore
|
||||
stagedEnclaves map[string]mpc.Enclave
|
||||
grpcAddr string
|
||||
}
|
||||
|
||||
func UseGateway(env hway.Hway, ipc common.IPFS, db *sql.DB) echo.MiddlewareFunc {
|
||||
func UseGateway(env hway.Hway, ipc common.IPFS, db *hwayorm.Queries) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
ua := useragent.NewParser()
|
||||
ctx := &GatewayContext{
|
||||
agent: ua.Parse(c.Request().UserAgent()),
|
||||
agent: ua.Parse(c.Request().UserAgent()),
|
||||
Context: c,
|
||||
dbq: repository.New(db),
|
||||
dbq: db,
|
||||
ipfsClient: ipc,
|
||||
grpcAddr: env.GetSonrGrpcUrl(),
|
||||
tokenStore: common.NewUCANStore(ipc),
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/internal/context"
|
||||
"github.com/onsonr/sonr/internal/database/repository"
|
||||
repository "github.com/onsonr/sonr/internal/models/drivers/hwayorm"
|
||||
)
|
||||
|
||||
func CheckHandleUnique(c echo.Context, handle string) bool {
|
||||
@@ -98,3 +98,28 @@ func DeleteProfile(c echo.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ Create Profile (/register/profile) │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
|
||||
// DefaultCreateProfileParams returns a default CreateProfileParams
|
||||
func DefaultCreateProfileParams() CreateProfileParams {
|
||||
return CreateProfileParams{
|
||||
TurnstileSiteKey: "",
|
||||
FirstNumber: 0,
|
||||
LastNumber: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateProfileParams represents the parameters for creating a profile
|
||||
type CreateProfileParams struct {
|
||||
TurnstileSiteKey string
|
||||
FirstNumber int
|
||||
LastNumber int
|
||||
}
|
||||
|
||||
// Sum returns the sum of the first and last number
|
||||
func (d CreateProfileParams) Sum() int {
|
||||
return d.FirstNumber + d.LastNumber
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ package middleware
|
||||
import (
|
||||
gocontext "context"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/internal/context"
|
||||
"github.com/onsonr/sonr/internal/database"
|
||||
"github.com/medama-io/go-useragent"
|
||||
ctx "github.com/onsonr/sonr/internal/context"
|
||||
"github.com/onsonr/sonr/internal/models/drivers/hwayorm"
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
func NewSession(c echo.Context) error {
|
||||
@@ -13,13 +16,13 @@ func NewSession(c echo.Context) error {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
baseSessionCreateParams := database.BaseSessionCreateParams(cc)
|
||||
baseSessionCreateParams := BaseSessionCreateParams(cc)
|
||||
cc.id = baseSessionCreateParams.ID
|
||||
if _, err := cc.dbq.CreateSession(bgCtx(), baseSessionCreateParams); err != nil {
|
||||
return err
|
||||
}
|
||||
// Set Cookie
|
||||
if err := context.WriteCookie(c, context.SessionID, cc.id); err != nil {
|
||||
if err := ctx.WriteCookie(c, ctx.SessionID, cc.id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -46,10 +49,10 @@ func GetSessionID(c echo.Context) string {
|
||||
}
|
||||
// check from cookie
|
||||
if cc.id == "" {
|
||||
if ok := context.CookieExists(c, context.SessionID); !ok {
|
||||
if ok := ctx.CookieExists(c, ctx.SessionID); !ok {
|
||||
return ""
|
||||
}
|
||||
cc.id = context.ReadCookieUnsafe(c, context.SessionID)
|
||||
cc.id = ctx.ReadCookieUnsafe(c, ctx.SessionID)
|
||||
}
|
||||
return cc.id
|
||||
}
|
||||
@@ -68,7 +71,7 @@ func GetSessionChallenge(c echo.Context) string {
|
||||
|
||||
func GetHandle(c echo.Context) string {
|
||||
// First check for the cookie
|
||||
handle := context.ReadCookieUnsafe(c, context.UserHandle)
|
||||
handle := ctx.ReadCookieUnsafe(c, ctx.UserHandle)
|
||||
if handle != "" {
|
||||
return handle
|
||||
}
|
||||
@@ -107,3 +110,50 @@ func bgCtx() gocontext.Context {
|
||||
ctx := gocontext.Background()
|
||||
return ctx
|
||||
}
|
||||
|
||||
func BaseSessionCreateParams(e echo.Context) hwayorm.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 hwayorm.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
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/internal/context"
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
"lukechampine.com/blake3"
|
||||
)
|
||||
|
||||
func Spawn(c echo.Context) (types.CreatePasskeyParams, error) {
|
||||
func Spawn(c echo.Context) (CreatePasskeyParams, error) {
|
||||
cc := c.(*GatewayContext)
|
||||
block := fmt.Sprintf("%d", CurrentBlock(c))
|
||||
handle := GetHandle(c)
|
||||
@@ -19,15 +18,15 @@ func Spawn(c echo.Context) (types.CreatePasskeyParams, error) {
|
||||
sid := GetSessionID(c)
|
||||
nonce, err := calcNonce(sid)
|
||||
if err != nil {
|
||||
return types.DefaultCreatePasskeyParams(), err
|
||||
return defaultCreatePasskeyParams(), err
|
||||
}
|
||||
encl, err := mpc.GenEnclave(nonce)
|
||||
if err != nil {
|
||||
return types.DefaultCreatePasskeyParams(), err
|
||||
return defaultCreatePasskeyParams(), err
|
||||
}
|
||||
cc.stagedEnclaves[sid] = encl
|
||||
context.WriteCookie(c, context.SonrAddress, encl.Address())
|
||||
return types.CreatePasskeyParams{
|
||||
return CreatePasskeyParams{
|
||||
Address: encl.Address(),
|
||||
Handle: handle,
|
||||
Name: origin,
|
||||
@@ -36,8 +35,8 @@ func Spawn(c echo.Context) (types.CreatePasskeyParams, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Claim() (types.CreatePasskeyParams, error) {
|
||||
return types.CreatePasskeyParams{}, nil
|
||||
func Claim() (CreatePasskeyParams, error) {
|
||||
return CreatePasskeyParams{}, nil
|
||||
}
|
||||
|
||||
// Uses blake3 to hash the sessionID to generate a nonce of length 12 bytes
|
||||
@@ -55,3 +54,27 @@ func calcNonce(sessionID string) ([]byte, error) {
|
||||
}
|
||||
return nonce, nil
|
||||
}
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ Create Passkey (/register/passkey) │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
|
||||
// defaultCreatePasskeyParams returns a default CreatePasskeyParams
|
||||
func defaultCreatePasskeyParams() CreatePasskeyParams {
|
||||
return CreatePasskeyParams{
|
||||
Address: "",
|
||||
Handle: "",
|
||||
Name: "",
|
||||
Challenge: "",
|
||||
CreationBlock: "",
|
||||
}
|
||||
}
|
||||
|
||||
// CreatePasskeyParams represents the parameters for creating a passkey
|
||||
type CreatePasskeyParams struct {
|
||||
Address string
|
||||
Handle string
|
||||
Name string
|
||||
Challenge string
|
||||
CreationBlock string
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
|
||||
func Register(e *echo.Echo) error {
|
||||
// Register View Handlers
|
||||
e.GET("/", handlers.RenderIndex)
|
||||
e.GET("/", handlers.HandleIndex)
|
||||
e.GET("/register", handlers.RenderProfileCreate)
|
||||
e.POST("/register/passkey", handlers.RenderPasskeyCreate)
|
||||
e.POST("/register/finish", handlers.RenderVaultLoading)
|
||||
|
||||
// Register Validation Handlers
|
||||
e.POST("/register/profile/handle", handlers.ValidateProfileHandle)
|
||||
e.POST("/register/profile/is_human", handlers.ValidateIsHumanSum)
|
||||
e.POST("/register/profile/handle", handlers.CheckProfileHandle)
|
||||
e.POST("/register/profile/is_human", handlers.CheckIsHumanSum)
|
||||
e.POST("/submit/profile/handle", handlers.SubmitProfileHandle)
|
||||
e.POST("/submit/credential", handlers.SubmitPublicKeyCredential)
|
||||
return nil
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ipfs/boxo/files"
|
||||
config "github.com/onsonr/sonr/internal/config/motr"
|
||||
"github.com/onsonr/sonr/internal/models"
|
||||
)
|
||||
|
||||
const SchemaVersion = 1
|
||||
const (
|
||||
AppManifestFileName = "app.webmanifest"
|
||||
DWNConfigFileName = "dwn.json"
|
||||
IndexHTMLFileName = "index.html"
|
||||
MainJSFileName = "main.js"
|
||||
ServiceWorkerFileName = "sw.js"
|
||||
)
|
||||
|
||||
// spawnVaultDirectory creates a new directory with the default files
|
||||
func NewVaultFS(cfg *config.Config) (files.Directory, error) {
|
||||
manifestBz, err := models.NewWebManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cnfBz, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files.NewMapDirectory(map[string]files.Node{
|
||||
AppManifestFileName: files.NewBytesFile(manifestBz),
|
||||
DWNConfigFileName: files.NewBytesFile(cnfBz),
|
||||
IndexHTMLFileName: files.NewBytesFile(IndexHTML),
|
||||
MainJSFileName: files.NewBytesFile(MainJS),
|
||||
ServiceWorkerFileName: files.NewBytesFile(WorkerJS),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// NewVaultConfig returns the default vault config
|
||||
func NewVaultConfig(addr string, ucanCID string) *config.Config {
|
||||
return &config.Config{
|
||||
MotrToken: ucanCID,
|
||||
MotrAddress: addr,
|
||||
IpfsGatewayUrl: "http://localhost:80",
|
||||
SonrApiUrl: "http://localhost:1317",
|
||||
SonrRpcUrl: "http://localhost:26657",
|
||||
SonrChainId: "sonr-testnet-1",
|
||||
VaultSchema: DefaultSchema(),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultSchema returns the default schema
|
||||
func DefaultSchema() *config.Schema {
|
||||
return &config.Schema{
|
||||
Version: SchemaVersion,
|
||||
Account: getSchema(&models.Account{}),
|
||||
Asset: getSchema(&models.Asset{}),
|
||||
Chain: getSchema(&models.Chain{}),
|
||||
Credential: getSchema(&models.Credential{}),
|
||||
Grant: getSchema(&models.Grant{}),
|
||||
Keyshare: getSchema(&models.Keyshare{}),
|
||||
Profile: getSchema(&models.Profile{}),
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sonr DWN</title>
|
||||
|
||||
<!-- HTMX -->
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
|
||||
<!-- WASM Support -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js"></script>
|
||||
|
||||
<!-- Main JS -->
|
||||
<script src="main.js"></script>
|
||||
|
||||
<!-- Tailwind (assuming you're using it based on your classes) -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<!-- Add manifest for PWA support -->
|
||||
<link
|
||||
rel="manifest"
|
||||
href="/app.webmanifest"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
|
||||
<!-- Offline detection styles -->
|
||||
<style>
|
||||
.offline-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.offline .offline-indicator {
|
||||
display: block;
|
||||
background: #f44336;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class="flex items-center justify-center h-full bg-zinc-50 lg:p-24 md:16 p-4"
|
||||
>
|
||||
<!-- Offline indicator -->
|
||||
<div class="offline-indicator">
|
||||
You are currently offline. Some features may be limited.
|
||||
</div>
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<div
|
||||
id="loading-indicator"
|
||||
class="fixed top-0 left-0 w-full h-1 bg-blue-200 transition-all duration-300"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="h-full bg-blue-600 w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
|
||||
<main
|
||||
class="flex-row items-center justify-center mx-auto w-fit max-w-screen-sm gap-y-3"
|
||||
>
|
||||
<div
|
||||
id="content"
|
||||
hx-get="/#"
|
||||
hx-trigger="load"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#loading-indicator"
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- WASM Ready Indicator (hidden) -->
|
||||
<div
|
||||
id="wasm-status"
|
||||
class="hidden fixed bottom-4 right-4 p-2 rounded-md bg-green-500 text-white"
|
||||
hx-swap-oob="true"
|
||||
>
|
||||
WASM Ready
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Initialize service worker
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", async function () {
|
||||
try {
|
||||
const registration =
|
||||
await navigator.serviceWorker.register("/sw.js");
|
||||
console.log(
|
||||
"Service Worker registered with scope:",
|
||||
registration.scope,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Service Worker registration failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// HTMX loading indicator
|
||||
htmx.on("htmx:beforeRequest", function (evt) {
|
||||
document.getElementById("loading-indicator").style.display = "block";
|
||||
});
|
||||
|
||||
htmx.on("htmx:afterRequest", function (evt) {
|
||||
document.getElementById("loading-indicator").style.display = "none";
|
||||
});
|
||||
|
||||
// WASM ready event handler
|
||||
document.addEventListener("wasm-ready", function () {
|
||||
const status = document.getElementById("wasm-status");
|
||||
status.classList.remove("hidden");
|
||||
setTimeout(() => {
|
||||
status.classList.add("hidden");
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// Offline status handler
|
||||
window.addEventListener("offline", function () {
|
||||
document.body.classList.add("offline");
|
||||
});
|
||||
|
||||
window.addEventListener("online", function () {
|
||||
document.body.classList.remove("offline");
|
||||
});
|
||||
|
||||
// Initial offline check
|
||||
if (!navigator.onLine) {
|
||||
document.body.classList.add("offline");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,152 +0,0 @@
|
||||
// MessageChannel for WASM communication
|
||||
let wasmChannel;
|
||||
let wasmPort;
|
||||
|
||||
async function initWasmChannel() {
|
||||
wasmChannel = new MessageChannel();
|
||||
wasmPort = wasmChannel.port1;
|
||||
|
||||
// Setup message handling from WASM
|
||||
wasmPort.onmessage = (event) => {
|
||||
const { type, data } = event.data;
|
||||
switch (type) {
|
||||
case 'WASM_READY':
|
||||
console.log('WASM is ready');
|
||||
document.dispatchEvent(new CustomEvent('wasm-ready'));
|
||||
break;
|
||||
case 'RESPONSE':
|
||||
handleWasmResponse(data);
|
||||
break;
|
||||
case 'SYNC_COMPLETE':
|
||||
handleSyncComplete(data);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize WebAssembly and Service Worker
|
||||
async function init() {
|
||||
try {
|
||||
// Register service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registration = await navigator.serviceWorker.register('./sw.js');
|
||||
console.log('ServiceWorker registered');
|
||||
|
||||
// Wait for the service worker to be ready
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
// Initialize MessageChannel
|
||||
await initWasmChannel();
|
||||
|
||||
// Send the MessageChannel port to the service worker
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
type: 'PORT_INITIALIZATION',
|
||||
port: wasmChannel.port2
|
||||
}, [wasmChannel.port2]);
|
||||
|
||||
// Register for periodic sync if available
|
||||
if ('periodicSync' in registration) {
|
||||
try {
|
||||
await registration.periodicSync.register('wasm-sync', {
|
||||
minInterval: 24 * 60 * 60 * 1000 // 24 hours
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('Periodic sync could not be registered:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize HTMX with custom config
|
||||
htmx.config.withCredentials = true;
|
||||
htmx.config.wsReconnectDelay = 'full-jitter';
|
||||
|
||||
// Override HTMX's internal request handling
|
||||
htmx.config.beforeRequest = function (config) {
|
||||
// Add request ID for tracking
|
||||
const requestId = 'req_' + Date.now();
|
||||
config.headers['X-Wasm-Request-ID'] = requestId;
|
||||
|
||||
// If offline, handle through service worker
|
||||
if (!navigator.onLine) {
|
||||
return false; // Let service worker handle it
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle HTMX after request
|
||||
htmx.config.afterRequest = function (config) {
|
||||
// Additional processing after request if needed
|
||||
};
|
||||
|
||||
// Handle HTMX errors
|
||||
htmx.config.errorHandler = function (error) {
|
||||
console.error('HTMX Error:', error);
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Initialization failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleWasmResponse(data) {
|
||||
const { requestId, response } = data;
|
||||
// Process the WASM response
|
||||
// This might update the UI or trigger HTMX swaps
|
||||
const targetElement = document.querySelector(`[data-request-id="${requestId}"]`);
|
||||
if (targetElement) {
|
||||
htmx.process(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSyncComplete(data) {
|
||||
const { url } = data;
|
||||
// Handle successful sync
|
||||
// Maybe refresh the relevant part of the UI
|
||||
htmx.trigger('body', 'sync:complete', { url });
|
||||
}
|
||||
|
||||
// Handle offline status changes
|
||||
window.addEventListener('online', () => {
|
||||
document.body.classList.remove('offline');
|
||||
// Trigger sync when back online
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({ type: 'SYNC_REQUEST' });
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
document.body.classList.add('offline');
|
||||
});
|
||||
|
||||
// Custom event handlers for HTMX
|
||||
document.addEventListener('htmx:beforeRequest', (event) => {
|
||||
const { elt, xhr } = event.detail;
|
||||
// Add request tracking
|
||||
const requestId = xhr.headers['X-Wasm-Request-ID'];
|
||||
elt.setAttribute('data-request-id', requestId);
|
||||
});
|
||||
|
||||
document.addEventListener('htmx:afterRequest', (event) => {
|
||||
const { elt, successful } = event.detail;
|
||||
if (successful) {
|
||||
elt.removeAttribute('data-request-id');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize everything when the page loads
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
// Export functions that might be needed by WASM
|
||||
window.wasmBridge = {
|
||||
triggerUIUpdate: function (selector, content) {
|
||||
const target = document.querySelector(selector);
|
||||
if (target) {
|
||||
htmx.process(htmx.parse(content).forEach(node => target.appendChild(node)));
|
||||
}
|
||||
},
|
||||
|
||||
showNotification: function (message, type = 'info') {
|
||||
// Implement notification system
|
||||
console.log(`${type}: ${message}`);
|
||||
}
|
||||
};
|
||||
@@ -1,258 +0,0 @@
|
||||
// Cache names for different types of resources
|
||||
const CACHE_NAMES = {
|
||||
wasm: 'wasm-cache-v1',
|
||||
static: 'static-cache-v1',
|
||||
dynamic: 'dynamic-cache-v1'
|
||||
};
|
||||
|
||||
// Import required scripts
|
||||
importScripts(
|
||||
"https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js",
|
||||
"https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v1.1.0/sw.js",
|
||||
);
|
||||
|
||||
// Initialize WASM HTTP listener
|
||||
const wasmInstance = registerWasmHTTPListener("https://cdn.sonr.id/wasm/app.wasm");
|
||||
|
||||
// MessageChannel port for WASM communication
|
||||
let wasmPort;
|
||||
|
||||
// Request queue for offline operations
|
||||
let requestQueue = new Map();
|
||||
|
||||
// Setup message channel handler
|
||||
self.addEventListener('message', async (event) => {
|
||||
if (event.data.type === 'PORT_INITIALIZATION') {
|
||||
wasmPort = event.data.port;
|
||||
setupWasmCommunication();
|
||||
}
|
||||
});
|
||||
|
||||
function setupWasmCommunication() {
|
||||
wasmPort.onmessage = async (event) => {
|
||||
const { type, data } = event.data;
|
||||
|
||||
switch (type) {
|
||||
case 'WASM_REQUEST':
|
||||
handleWasmRequest(data);
|
||||
break;
|
||||
case 'SYNC_REQUEST':
|
||||
processSyncQueue();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Notify that WASM is ready
|
||||
wasmPort.postMessage({ type: 'WASM_READY' });
|
||||
}
|
||||
|
||||
// Enhanced install event
|
||||
self.addEventListener("install", (event) => {
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
skipWaiting(),
|
||||
// Cache WASM binary and essential resources
|
||||
caches.open(CACHE_NAMES.wasm).then(cache =>
|
||||
cache.addAll([
|
||||
'https://cdn.sonr.id/wasm/app.wasm',
|
||||
'https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js'
|
||||
])
|
||||
)
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
// Enhanced activate event
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
clients.claim(),
|
||||
// Clean up old caches
|
||||
caches.keys().then(keys =>
|
||||
Promise.all(
|
||||
keys.map(key => {
|
||||
if (!Object.values(CACHE_NAMES).includes(key)) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
// Intercept fetch events
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const request = event.request;
|
||||
|
||||
// Handle API requests differently from static resources
|
||||
if (request.url.includes('/api/')) {
|
||||
event.respondWith(handleApiRequest(request));
|
||||
} else {
|
||||
event.respondWith(handleStaticRequest(request));
|
||||
}
|
||||
});
|
||||
|
||||
async function handleApiRequest(request) {
|
||||
try {
|
||||
// Try to make the request
|
||||
const response = await fetch(request.clone());
|
||||
|
||||
// If successful, pass through WASM handler
|
||||
if (response.ok) {
|
||||
return await processWasmResponse(request, response);
|
||||
}
|
||||
|
||||
// If offline or failed, queue the request
|
||||
await queueRequest(request);
|
||||
|
||||
// Return cached response if available
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return offline response
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Currently offline' }),
|
||||
{
|
||||
status: 503,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
await queueRequest(request);
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Request failed' }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStaticRequest(request) {
|
||||
// Check cache first
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(request);
|
||||
|
||||
// Cache successful responses
|
||||
if (response.ok) {
|
||||
const cache = await caches.open(CACHE_NAMES.static);
|
||||
cache.put(request, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Return offline page for navigation requests
|
||||
if (request.mode === 'navigate') {
|
||||
return caches.match('/offline.html');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function processWasmResponse(request, response) {
|
||||
// Clone the response before processing
|
||||
const responseClone = response.clone();
|
||||
|
||||
try {
|
||||
// Process through WASM
|
||||
const processedResponse = await wasmInstance.processResponse(responseClone);
|
||||
|
||||
// Notify client through message channel
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({
|
||||
type: 'RESPONSE',
|
||||
requestId: request.headers.get('X-Wasm-Request-ID'),
|
||||
response: processedResponse
|
||||
});
|
||||
}
|
||||
|
||||
return processedResponse;
|
||||
} catch (error) {
|
||||
console.error('WASM processing error:', error);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
async function queueRequest(request) {
|
||||
const serializedRequest = await serializeRequest(request);
|
||||
requestQueue.set(request.url, serializedRequest);
|
||||
|
||||
// Register for background sync
|
||||
try {
|
||||
await self.registration.sync.register('wasm-sync');
|
||||
} catch (error) {
|
||||
console.error('Sync registration failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function serializeRequest(request) {
|
||||
const headers = {};
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
headers[key] = value;
|
||||
}
|
||||
|
||||
return {
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers,
|
||||
body: await request.text(),
|
||||
timestamp: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
// Handle background sync
|
||||
self.addEventListener('sync', (event) => {
|
||||
if (event.tag === 'wasm-sync') {
|
||||
event.waitUntil(processSyncQueue());
|
||||
}
|
||||
});
|
||||
|
||||
async function processSyncQueue() {
|
||||
const requests = Array.from(requestQueue.values());
|
||||
|
||||
for (const serializedRequest of requests) {
|
||||
try {
|
||||
const response = await fetch(new Request(serializedRequest.url, {
|
||||
method: serializedRequest.method,
|
||||
headers: serializedRequest.headers,
|
||||
body: serializedRequest.body
|
||||
}));
|
||||
|
||||
if (response.ok) {
|
||||
requestQueue.delete(serializedRequest.url);
|
||||
|
||||
// Notify client of successful sync
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({
|
||||
type: 'SYNC_COMPLETE',
|
||||
url: serializedRequest.url
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Sync failed for request:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle payment requests
|
||||
self.addEventListener("canmakepayment", function (e) {
|
||||
e.respondWith(Promise.resolve(true));
|
||||
});
|
||||
|
||||
// Handle periodic sync if available
|
||||
self.addEventListener('periodicsync', (event) => {
|
||||
if (event.tag === 'wasm-sync') {
|
||||
event.waitUntil(processSyncQueue());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed index.html
|
||||
var IndexHTML []byte
|
||||
|
||||
//go:embed main.js
|
||||
var MainJS []byte
|
||||
|
||||
//go:embed sw.js
|
||||
var WorkerJS []byte
|
||||
|
||||
func getSchema(structType interface{}) string {
|
||||
t := reflect.TypeOf(structType)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return ""
|
||||
}
|
||||
|
||||
var fields []string
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
fieldName := toCamelCase(field.Name)
|
||||
fields = append(fields, fieldName)
|
||||
}
|
||||
|
||||
// Add "++" at the beginning, separated by a comma
|
||||
return "++, " + strings.Join(fields, ", ")
|
||||
}
|
||||
|
||||
func toCamelCase(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if len(s) == 1 {
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package types
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ Create Passkey (/register/passkey) │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
|
||||
// DefaultCreatePasskeyParams returns a default CreatePasskeyParams
|
||||
func DefaultCreatePasskeyParams() CreatePasskeyParams {
|
||||
return CreatePasskeyParams{
|
||||
Address: "",
|
||||
Handle: "",
|
||||
Name: "",
|
||||
Challenge: "",
|
||||
CreationBlock: "",
|
||||
}
|
||||
}
|
||||
|
||||
// CreatePasskeyParams represents the parameters for creating a passkey
|
||||
type CreatePasskeyParams struct {
|
||||
Address string
|
||||
Handle string
|
||||
Name string
|
||||
Challenge string
|
||||
CreationBlock string
|
||||
}
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ Create Profile (/register/profile) │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
|
||||
// DefaultCreateProfileParams returns a default CreateProfileParams
|
||||
func DefaultCreateProfileParams() CreateProfileParams {
|
||||
return CreateProfileParams{
|
||||
TurnstileSiteKey: "",
|
||||
FirstNumber: 0,
|
||||
LastNumber: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateProfileParams represents the parameters for creating a profile
|
||||
type CreateProfileParams struct {
|
||||
TurnstileSiteKey string
|
||||
FirstNumber int
|
||||
LastNumber int
|
||||
}
|
||||
|
||||
// Sum returns the sum of the first and last number
|
||||
func (d CreateProfileParams) Sum() int {
|
||||
return d.FirstNumber + d.LastNumber
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package types
|
||||
|
||||
import "github.com/onsonr/sonr/internal/database/repository"
|
||||
|
||||
// Define the credential structure matching our frontend data
|
||||
type CredentialDescriptor struct {
|
||||
ID string `json:"id"`
|
||||
RawID string `json:"rawId"`
|
||||
Type string `json:"type"`
|
||||
AuthenticatorAttachment string `json:"authenticatorAttachment"`
|
||||
Transports string `json:"transports"`
|
||||
ClientExtensionResults map[string]string `json:"clientExtensionResults"`
|
||||
Response struct {
|
||||
AttestationObject string `json:"attestationObject"`
|
||||
ClientDataJSON string `json:"clientDataJSON"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
func (c *CredentialDescriptor) ToModel(handle, origin string) *repository.Credential {
|
||||
return &repository.Credential{
|
||||
Handle: handle,
|
||||
Origin: origin,
|
||||
CredentialID: c.ID,
|
||||
Type: c.Type,
|
||||
Transports: c.Transports,
|
||||
AuthenticatorAttachment: c.AuthenticatorAttachment,
|
||||
}
|
||||
}
|
||||
|
||||
func CredentialArrayToDescriptors(credentials []repository.Credential) []*CredentialDescriptor {
|
||||
var descriptors []*CredentialDescriptor
|
||||
for _, cred := range credentials {
|
||||
cd := &CredentialDescriptor{
|
||||
ID: cred.CredentialID,
|
||||
RawID: cred.CredentialID,
|
||||
Type: cred.Type,
|
||||
AuthenticatorAttachment: cred.AuthenticatorAttachment,
|
||||
Transports: cred.Transports,
|
||||
}
|
||||
descriptors = append(descriptors, cd)
|
||||
}
|
||||
return descriptors
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
"github.com/onsonr/sonr/internal/nebula/card"
|
||||
"github.com/onsonr/sonr/internal/nebula/form"
|
||||
"github.com/onsonr/sonr/internal/nebula/hero"
|
||||
@@ -9,7 +8,7 @@ import (
|
||||
"github.com/onsonr/sonr/internal/nebula/layout"
|
||||
)
|
||||
|
||||
templ RegisterProfileView(data types.CreateProfileParams) {
|
||||
templ RegisterProfileView(firstNumber int, lastNumber int) {
|
||||
@layout.View("New Profile | Sonr.ID") {
|
||||
@layout.Container() {
|
||||
@hero.TitleDesc("Basic Info", "Tell us a little about yourself.")
|
||||
@@ -23,7 +22,7 @@ templ RegisterProfileView(data types.CreateProfileParams) {
|
||||
}
|
||||
@input.Handle()
|
||||
@input.Name()
|
||||
@input.HumanSlider(data.FirstNumber, data.LastNumber)
|
||||
@input.HumanSlider(firstNumber, lastNumber)
|
||||
@form.Footer() {
|
||||
@form.CancelButton()
|
||||
@form.SubmitButton("Next")
|
||||
@@ -34,7 +33,7 @@ templ RegisterProfileView(data types.CreateProfileParams) {
|
||||
}
|
||||
}
|
||||
|
||||
templ RegisterPasskeyView(data types.CreatePasskeyParams) {
|
||||
templ RegisterPasskeyView(address string, handle string, name string, challenge string, creationBlock string) {
|
||||
@layout.View("Register | Sonr.ID") {
|
||||
@layout.Container() {
|
||||
@hero.TitleDesc("Link a PassKey", "This will be used to login to your vault.")
|
||||
@@ -42,11 +41,11 @@ templ RegisterPasskeyView(data types.CreatePasskeyParams) {
|
||||
<input type="hidden" name="credential" id="credential-data" required/>
|
||||
@form.Body() {
|
||||
@form.Header() {
|
||||
@card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock)
|
||||
@card.SonrProfile(address, name, handle, creationBlock)
|
||||
}
|
||||
@input.CoinSelect()
|
||||
@form.Footer() {
|
||||
@input.Passkey(data.Address, data.Handle, data.Challenge)
|
||||
@input.Passkey(address, handle, challenge)
|
||||
@form.CancelButton()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@ import (
|
||||
"github.com/onsonr/sonr/internal/nebula/hero"
|
||||
"github.com/onsonr/sonr/internal/nebula/input"
|
||||
"github.com/onsonr/sonr/internal/nebula/layout"
|
||||
"github.com/onsonr/sonr/pkg/gateway/types"
|
||||
)
|
||||
|
||||
func RegisterProfileView(data types.CreateProfileParams) templ.Component {
|
||||
func RegisterProfileView(firstNumber int, lastNumber int) 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 {
|
||||
@@ -136,7 +135,7 @@ func RegisterProfileView(data types.CreateProfileParams) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = input.HumanSlider(data.FirstNumber, data.LastNumber).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = input.HumanSlider(firstNumber, lastNumber).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -202,7 +201,7 @@ func RegisterProfileView(data types.CreateProfileParams) templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func RegisterPasskeyView(data types.CreatePasskeyParams) templ.Component {
|
||||
func RegisterPasskeyView(address string, handle string, name string, challenge string, creationBlock string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@@ -295,7 +294,7 @@ func RegisterPasskeyView(data types.CreatePasskeyParams) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = card.SonrProfile(address, name, handle, creationBlock).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -329,7 +328,7 @@ func RegisterPasskeyView(data types.CreatePasskeyParams) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = input.Passkey(data.Address, data.Handle, data.Challenge).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = input.Passkey(address, handle, challenge).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user