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:
Prad Nukala
2024-12-18 20:53:45 +00:00
committed by GitHub
parent fc001216a8
commit 6072f6ecfa
111 changed files with 4919 additions and 8584 deletions
-2
View File
@@ -13,6 +13,4 @@ type Config struct {
SonrRpcUrl string `pkl:"sonrRpcUrl" json:"sonrRpcUrl,omitempty"`
SonrChainId string `pkl:"sonrChainId" json:"sonrChainId,omitempty"`
VaultSchema *Schema `pkl:"vaultSchema" json:"vaultSchema,omitempty"`
}
-22
View File
@@ -1,22 +0,0 @@
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
package motr
type Schema struct {
Version int `pkl:"version"`
Account string `pkl:"account" json:"account,omitempty"`
Asset string `pkl:"asset" json:"asset,omitempty"`
Chain string `pkl:"chain" json:"chain,omitempty"`
Credential string `pkl:"credential" json:"credential,omitempty"`
Jwk string `pkl:"jwk" json:"jwk,omitempty"`
Grant string `pkl:"grant" json:"grant,omitempty"`
Keyshare string `pkl:"keyshare" json:"keyshare,omitempty"`
Profile string `pkl:"profile" json:"profile,omitempty"`
}
-1
View File
@@ -6,6 +6,5 @@ import "github.com/apple/pkl-go/pkl"
func init() {
pkl.RegisterMapping("sonr.net.Motr", Motr{})
pkl.RegisterMapping("sonr.net.Motr#Config", Config{})
pkl.RegisterMapping("sonr.net.Motr#Schema", Schema{})
pkl.RegisterMapping("sonr.net.Motr#Environment", Environment{})
}
-25
View File
@@ -1,25 +0,0 @@
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
}
-58
View File
@@ -1,58 +0,0 @@
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
}
-9
View File
@@ -1,9 +0,0 @@
version: "2"
sql:
- engine: "sqlite"
queries: "./sink/query.sql"
schema: "./sink/schema.sql"
gen:
go:
package: "repository"
out: "repository"
+48
View File
@@ -0,0 +1,48 @@
package embed
import (
"encoding/json"
"github.com/ipfs/boxo/files"
"github.com/onsonr/sonr/internal/config/motr"
)
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 *motr.Config) (files.Directory, error) {
manifestBz, err := 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) *motr.Config {
return &motr.Config{
MotrToken: ucanCID,
MotrAddress: addr,
IpfsGatewayUrl: "http://localhost:80",
SonrApiUrl: "http://localhost:1317",
SonrRpcUrl: "http://localhost:26657",
SonrChainId: "sonr-testnet-1",
}
}
+138
View File
@@ -0,0 +1,138 @@
<!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>
+152
View File
@@ -0,0 +1,152 @@
// 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}`);
}
};
+258
View File
@@ -0,0 +1,258 @@
// 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());
}
});
+47
View File
@@ -0,0 +1,47 @@
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,4 +1,4 @@
package models
package embed
import "encoding/json"
-20
View File
@@ -1,20 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Account struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Name string `pkl:"name" json:"name,omitempty"`
Address any `pkl:"address" json:"address,omitempty"`
PublicKey string `pkl:"publicKey" json:"publicKey,omitempty"`
ChainCode uint `pkl:"chainCode" json:"chainCode,omitempty"`
Index int `pkl:"index" json:"index,omitempty"`
Controller string `pkl:"controller" json:"controller,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
}
-16
View File
@@ -1,16 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Asset struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Name string `pkl:"name" json:"name,omitempty"`
Symbol string `pkl:"symbol" json:"symbol,omitempty"`
Decimals int `pkl:"decimals" json:"decimals,omitempty"`
ChainCode uint `pkl:"chainCode" json:"chainCode,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
}
-14
View File
@@ -1,14 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Chain struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Name string `pkl:"name" json:"name,omitempty"`
NetworkId string `pkl:"networkId" json:"networkId,omitempty"`
ChainCode uint `pkl:"chainCode" json:"chainCode,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
}
-40
View File
@@ -1,40 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Credential struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Subject string `pkl:"subject" json:"subject,omitempty"`
Controller string `pkl:"controller" json:"controller,omitempty"`
AttestationType string `pkl:"attestationType" json:"attestationType,omitempty"`
Origin string `pkl:"origin" json:"origin,omitempty"`
Label *string `pkl:"label" json:"label,omitempty"`
DeviceId *string `pkl:"deviceId" json:"deviceId,omitempty"`
CredentialId string `pkl:"credentialId" json:"credentialId,omitempty"`
PublicKey string `pkl:"publicKey" json:"publicKey,omitempty"`
Transport []string `pkl:"transport" json:"transport,omitempty"`
SignCount uint `pkl:"signCount" json:"signCount,omitempty"`
UserPresent bool `pkl:"userPresent" json:"userPresent,omitempty"`
UserVerified bool `pkl:"userVerified" json:"userVerified,omitempty"`
BackupEligible bool `pkl:"backupEligible" json:"backupEligible,omitempty"`
BackupState bool `pkl:"backupState" json:"backupState,omitempty"`
CloneWarning bool `pkl:"cloneWarning" json:"cloneWarning,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
UpdatedAt *string `pkl:"updatedAt" json:"updatedAt,omitempty"`
}
-28
View File
@@ -1,28 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
import (
"github.com/onsonr/sonr/internal/models/keyalgorithm"
"github.com/onsonr/sonr/internal/models/keycurve"
"github.com/onsonr/sonr/internal/models/keyencoding"
"github.com/onsonr/sonr/internal/models/keyrole"
"github.com/onsonr/sonr/internal/models/keytype"
)
type DID struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Role keyrole.KeyRole `pkl:"role"`
Algorithm keyalgorithm.KeyAlgorithm `pkl:"algorithm"`
Encoding keyencoding.KeyEncoding `pkl:"encoding"`
Curve keycurve.KeyCurve `pkl:"curve"`
KeyType keytype.KeyType `pkl:"key_type"`
Raw string `pkl:"raw"`
Jwk *JWK `pkl:"jwk"`
}
-20
View File
@@ -1,20 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Grant struct {
Id uint `pkl:"id" json:"id,omitempty" query:"id"`
Subject string `pkl:"subject" json:"subject,omitempty"`
Controller string `pkl:"controller" json:"controller,omitempty"`
Origin string `pkl:"origin" json:"origin,omitempty"`
Token string `pkl:"token" json:"token,omitempty"`
Scopes []string `pkl:"scopes" json:"scopes,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
UpdatedAt *string `pkl:"updatedAt" json:"updatedAt,omitempty"`
}
-16
View File
@@ -1,16 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type JWK struct {
Kty string `pkl:"kty" json:"kty,omitempty"`
Crv string `pkl:"crv" json:"crv,omitempty"`
X string `pkl:"x" json:"x,omitempty"`
Y string `pkl:"y" json:"y,omitempty"`
N string `pkl:"n" json:"n,omitempty"`
E string `pkl:"e" json:"e,omitempty"`
}
-14
View File
@@ -1,14 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Keyshare struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Data string `pkl:"data" json:"data,omitempty"`
Role int `pkl:"role" json:"role,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
LastRefreshed *string `pkl:"lastRefreshed" json:"lastRefreshed,omitempty"`
}
-39
View File
@@ -1,39 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
import (
"context"
"github.com/apple/pkl-go/pkl"
)
type Models struct {
DbName string `pkl:"db_name"`
DbVersion int `pkl:"db_version"`
}
// LoadFromPath loads the pkl module at the given path and evaluates it into a Models
func LoadFromPath(ctx context.Context, path string) (ret *Models, err error) {
evaluator, err := pkl.NewEvaluator(ctx, pkl.PreconfiguredOptions)
if err != nil {
return nil, err
}
defer func() {
cerr := evaluator.Close()
if err == nil {
err = cerr
}
}()
ret, err = Load(ctx, evaluator, pkl.FileSource(path))
return ret, err
}
// Load loads the pkl module at the given source and evaluates it with the given evaluator into a Models
func Load(ctx context.Context, evaluator pkl.Evaluator, source *pkl.ModuleSource) (*Models, error) {
var ret Models
if err := evaluator.EvaluateModule(ctx, source, &ret); err != nil {
return nil, err
}
return &ret, nil
}
-20
View File
@@ -1,20 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
type Profile struct {
Id string `pkl:"id" json:"id,omitempty" query:"id"`
Subject string `pkl:"subject" json:"subject,omitempty"`
Controller string `pkl:"controller" json:"controller,omitempty"`
OriginUri *string `pkl:"originUri" json:"originUri,omitempty"`
PublicMetadata *string `pkl:"publicMetadata" json:"publicMetadata,omitempty"`
PrivateMetadata *string `pkl:"privateMetadata" json:"privateMetadata,omitempty"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty"`
UpdatedAt *string `pkl:"updatedAt" json:"updatedAt,omitempty"`
}
@@ -1,46 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package assettype
import (
"encoding"
"fmt"
)
type AssetType string
const (
Native AssetType = "native"
Wrapped AssetType = "wrapped"
Staking AssetType = "staking"
Pool AssetType = "pool"
Ibc AssetType = "ibc"
Cw20 AssetType = "cw20"
)
// String returns the string representation of AssetType
func (rcv AssetType) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(AssetType)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for AssetType.
func (rcv *AssetType) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "native":
*rcv = Native
case "wrapped":
*rcv = Wrapped
case "staking":
*rcv = Staking
case "pool":
*rcv = Pool
case "ibc":
*rcv = Ibc
case "cw20":
*rcv = Cw20
default:
return fmt.Errorf(`illegal: "%s" is not a valid AssetType`, str)
}
return nil
}
@@ -1,52 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package didmethod
import (
"encoding"
"fmt"
)
type DIDMethod string
const (
Ipfs DIDMethod = "ipfs"
Sonr DIDMethod = "sonr"
Bitcoin DIDMethod = "bitcoin"
Ethereum DIDMethod = "ethereum"
Ibc DIDMethod = "ibc"
Webauthn DIDMethod = "webauthn"
Dwn DIDMethod = "dwn"
Service DIDMethod = "service"
)
// String returns the string representation of DIDMethod
func (rcv DIDMethod) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(DIDMethod)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for DIDMethod.
func (rcv *DIDMethod) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "ipfs":
*rcv = Ipfs
case "sonr":
*rcv = Sonr
case "bitcoin":
*rcv = Bitcoin
case "ethereum":
*rcv = Ethereum
case "ibc":
*rcv = Ibc
case "webauthn":
*rcv = Webauthn
case "dwn":
*rcv = Dwn
case "service":
*rcv = Service
default:
return fmt.Errorf(`illegal: "%s" is not a valid DIDMethod`, str)
}
return nil
}
+32
View File
@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package hwayorm
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}
+98
View File
@@ -0,0 +1,98 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package hwayorm
import (
"github.com/jackc/pgx/v5/pgtype"
)
type Account struct {
ID string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Number int64
Sequence int32
Address string
PublicKey string
ChainID string
Controller string
IsSubsidiary bool
IsValidator bool
IsDelegator bool
IsAccountable bool
}
type Asset struct {
ID string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Name string
Symbol string
Decimals int32
ChainID string
Channel string
AssetType string
CoingeckoID pgtype.Text
}
type Credential struct {
ID string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Handle string
CredentialID string
AuthenticatorAttachment string
Origin string
Type string
Transports string
}
type Profile struct {
ID string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Address string
Handle string
Origin string
Name string
}
type Session struct {
ID string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
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 string
}
type Vault struct {
ID int64
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Handle string
Origin string
Address string
Cid string
Config []byte
SessionID int64
RedirectUri string
}
@@ -0,0 +1,578 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: query.sql
package hwayorm
import (
"context"
)
const checkHandleExists = `-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = $1
AND deleted_at IS NULL
`
func (q *Queries) CheckHandleExists(ctx context.Context, handle string) (bool, error) {
row := q.db.QueryRow(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 ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
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 string
}
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
row := q.db.QueryRow(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 = $1 AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetChallengeBySessionID(ctx context.Context, id string) (string, error) {
row := q.db.QueryRow(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 = $1
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetCredentialByID(ctx context.Context, credentialID string) (Credential, error) {
row := q.db.QueryRow(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 = $1
AND deleted_at IS NULL
`
func (q *Queries) GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error) {
rows, err := q.db.Query(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.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 = $1 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.QueryRow(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 = $1 AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByAddress(ctx context.Context, address string) (Profile, error) {
row := q.db.QueryRow(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 = $1
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByHandle(ctx context.Context, handle string) (Profile, error) {
row := q.db.QueryRow(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 = $1 AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByID(ctx context.Context, id string) (Profile, error) {
row := q.db.QueryRow(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 = $1 AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error) {
row := q.db.QueryRow(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 = $1 AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error) {
row := q.db.QueryRow(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 = $1
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error) {
row := q.db.QueryRow(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 = $1
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetVaultRedirectURIBySessionID(ctx context.Context, sessionID int64) (string, error) {
row := q.db.QueryRow(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 ($1, $2, $3, $4, $5)
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.QueryRow(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 ($1, $2, $3, $4)
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.QueryRow(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 = $1
`
func (q *Queries) SoftDeleteCredential(ctx context.Context, credentialID string) error {
_, err := q.db.Exec(ctx, softDeleteCredential, credentialID)
return err
}
const softDeleteProfile = `-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = $1
`
func (q *Queries) SoftDeleteProfile(ctx context.Context, address string) error {
_, err := q.db.Exec(ctx, softDeleteProfile, address)
return err
}
const updateProfile = `-- name: UpdateProfile :one
UPDATE profiles
SET
name = $1,
handle = $2,
updated_at = CURRENT_TIMESTAMP
WHERE address = $3
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.QueryRow(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 = $1,
is_human_last = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3
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.QueryRow(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 = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2
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 string
ID string
}
func (q *Queries) UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error) {
row := q.db.QueryRow(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
}
@@ -2,7 +2,7 @@
// versions:
// sqlc v1.27.0
package repository
package motrorm
import (
"context"
@@ -2,7 +2,7 @@
// versions:
// sqlc v1.27.0
package repository
package motrorm
import (
"database/sql"
@@ -41,7 +41,7 @@ type Asset struct {
}
type Credential struct {
ID int64
ID string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt sql.NullTime
@@ -54,7 +54,7 @@ type Credential struct {
}
type Profile struct {
ID int64
ID string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt sql.NullTime
@@ -85,7 +85,7 @@ type Session struct {
}
type Vault struct {
ID int64
ID string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt sql.NullTime
@@ -3,7 +3,7 @@
// sqlc v1.27.0
// source: query.sql
package repository
package motrorm
import (
"context"
@@ -246,7 +246,7 @@ WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByID(ctx context.Context, id int64) (Profile, error) {
func (q *Queries) GetProfileByID(ctx context.Context, id string) (Profile, error) {
row := q.db.QueryRowContext(ctx, getProfileByID, id)
var i Profile
err := row.Scan(
-17
View File
@@ -1,17 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package models
import "github.com/apple/pkl-go/pkl"
func init() {
pkl.RegisterMapping("sonr.orm.Models", Models{})
pkl.RegisterMapping("sonr.orm.Models#Account", Account{})
pkl.RegisterMapping("sonr.orm.Models#Asset", Asset{})
pkl.RegisterMapping("sonr.orm.Models#Chain", Chain{})
pkl.RegisterMapping("sonr.orm.Models#Credential", Credential{})
pkl.RegisterMapping("sonr.orm.Models#DID", DID{})
pkl.RegisterMapping("sonr.orm.Models#JWK", JWK{})
pkl.RegisterMapping("sonr.orm.Models#Grant", Grant{})
pkl.RegisterMapping("sonr.orm.Models#Keyshare", Keyshare{})
pkl.RegisterMapping("sonr.orm.Models#Profile", Profile{})
}
@@ -1,46 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keyalgorithm
import (
"encoding"
"fmt"
)
type KeyAlgorithm string
const (
Es256 KeyAlgorithm = "es256"
Es384 KeyAlgorithm = "es384"
Es512 KeyAlgorithm = "es512"
Eddsa KeyAlgorithm = "eddsa"
Es256k KeyAlgorithm = "es256k"
Ecdsa KeyAlgorithm = "ecdsa"
)
// String returns the string representation of KeyAlgorithm
func (rcv KeyAlgorithm) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyAlgorithm)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyAlgorithm.
func (rcv *KeyAlgorithm) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "es256":
*rcv = Es256
case "es384":
*rcv = Es384
case "es512":
*rcv = Es512
case "eddsa":
*rcv = Eddsa
case "es256k":
*rcv = Es256k
case "ecdsa":
*rcv = Ecdsa
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyAlgorithm`, str)
}
return nil
}
-58
View File
@@ -1,58 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keycurve
import (
"encoding"
"fmt"
)
type KeyCurve string
const (
P256 KeyCurve = "p256"
P384 KeyCurve = "p384"
P521 KeyCurve = "p521"
X25519 KeyCurve = "x25519"
X448 KeyCurve = "x448"
Ed25519 KeyCurve = "ed25519"
Ed448 KeyCurve = "ed448"
Secp256k1 KeyCurve = "secp256k1"
Bls12381 KeyCurve = "bls12381"
Keccak256 KeyCurve = "keccak256"
)
// String returns the string representation of KeyCurve
func (rcv KeyCurve) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyCurve)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyCurve.
func (rcv *KeyCurve) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "p256":
*rcv = P256
case "p384":
*rcv = P384
case "p521":
*rcv = P521
case "x25519":
*rcv = X25519
case "x448":
*rcv = X448
case "ed25519":
*rcv = Ed25519
case "ed448":
*rcv = Ed448
case "secp256k1":
*rcv = Secp256k1
case "bls12381":
*rcv = Bls12381
case "keccak256":
*rcv = Keccak256
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyCurve`, str)
}
return nil
}
@@ -1,37 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keyencoding
import (
"encoding"
"fmt"
)
type KeyEncoding string
const (
Raw KeyEncoding = "raw"
Hex KeyEncoding = "hex"
Multibase KeyEncoding = "multibase"
)
// String returns the string representation of KeyEncoding
func (rcv KeyEncoding) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyEncoding)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyEncoding.
func (rcv *KeyEncoding) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "raw":
*rcv = Raw
case "hex":
*rcv = Hex
case "multibase":
*rcv = Multibase
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyEncoding`, str)
}
return nil
}
-40
View File
@@ -1,40 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keyrole
import (
"encoding"
"fmt"
)
type KeyRole string
const (
Authentication KeyRole = "authentication"
Assertion KeyRole = "assertion"
Delegation KeyRole = "delegation"
Invocation KeyRole = "invocation"
)
// String returns the string representation of KeyRole
func (rcv KeyRole) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyRole)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyRole.
func (rcv *KeyRole) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "authentication":
*rcv = Authentication
case "assertion":
*rcv = Assertion
case "delegation":
*rcv = Delegation
case "invocation":
*rcv = Invocation
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyRole`, str)
}
return nil
}
@@ -1,34 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keysharerole
import (
"encoding"
"fmt"
)
type KeyShareRole string
const (
User KeyShareRole = "user"
Validator KeyShareRole = "validator"
)
// String returns the string representation of KeyShareRole
func (rcv KeyShareRole) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyShareRole)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyShareRole.
func (rcv *KeyShareRole) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "user":
*rcv = User
case "validator":
*rcv = Validator
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyShareRole`, str)
}
return nil
}
-55
View File
@@ -1,55 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package keytype
import (
"encoding"
"fmt"
)
type KeyType string
const (
Octet KeyType = "octet"
Elliptic KeyType = "elliptic"
Rsa KeyType = "rsa"
Symmetric KeyType = "symmetric"
Hmac KeyType = "hmac"
Mpc KeyType = "mpc"
Zk KeyType = "zk"
Webauthn KeyType = "webauthn"
Bip32 KeyType = "bip32"
)
// String returns the string representation of KeyType
func (rcv KeyType) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(KeyType)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for KeyType.
func (rcv *KeyType) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "octet":
*rcv = Octet
case "elliptic":
*rcv = Elliptic
case "rsa":
*rcv = Rsa
case "symmetric":
*rcv = Symmetric
case "hmac":
*rcv = Hmac
case "mpc":
*rcv = Mpc
case "zk":
*rcv = Zk
case "webauthn":
*rcv = Webauthn
case "bip32":
*rcv = Bip32
default:
return fmt.Errorf(`illegal: "%s" is not a valid KeyType`, str)
}
return nil
}
@@ -1,46 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package permissiongrant
import (
"encoding"
"fmt"
)
type PermissionGrant string
const (
None PermissionGrant = "none"
Read PermissionGrant = "read"
Write PermissionGrant = "write"
Verify PermissionGrant = "verify"
Broadcast PermissionGrant = "broadcast"
Admin PermissionGrant = "admin"
)
// String returns the string representation of PermissionGrant
func (rcv PermissionGrant) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(PermissionGrant)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for PermissionGrant.
func (rcv *PermissionGrant) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "none":
*rcv = None
case "read":
*rcv = Read
case "write":
*rcv = Write
case "verify":
*rcv = Verify
case "broadcast":
*rcv = Broadcast
case "admin":
*rcv = Admin
default:
return fmt.Errorf(`illegal: "%s" is not a valid PermissionGrant`, str)
}
return nil
}
@@ -1,49 +0,0 @@
// Code generated from Pkl module `sonr.orm.Models`. DO NOT EDIT.
package permissionscope
import (
"encoding"
"fmt"
)
type PermissionScope string
const (
Profile PermissionScope = "profile"
Metadata PermissionScope = "metadata"
Permissions PermissionScope = "permissions"
Wallets PermissionScope = "wallets"
Transactions PermissionScope = "transactions"
User PermissionScope = "user"
Validator PermissionScope = "validator"
)
// String returns the string representation of PermissionScope
func (rcv PermissionScope) String() string {
return string(rcv)
}
var _ encoding.BinaryUnmarshaler = new(PermissionScope)
// UnmarshalBinary implements encoding.BinaryUnmarshaler for PermissionScope.
func (rcv *PermissionScope) UnmarshalBinary(data []byte) error {
switch str := string(data); str {
case "profile":
*rcv = Profile
case "metadata":
*rcv = Metadata
case "permissions":
*rcv = Permissions
case "wallets":
*rcv = Wallets
case "transactions":
*rcv = Transactions
case "user":
*rcv = User
case "validator":
*rcv = Validator
default:
return fmt.Errorf(`illegal: "%s" is not a valid PermissionScope`, str)
}
return nil
}
+138
View File
@@ -0,0 +1,138 @@
-- name: InsertCredential :one
INSERT INTO credentials (
handle,
credential_id,
origin,
type,
transports
) VALUES ($1, $2, $3, $4, $5)
RETURNING *;
-- name: InsertProfile :one
INSERT INTO profiles (
address,
handle,
origin,
name
) VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetProfileByID :one
SELECT * FROM profiles
WHERE id = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByAddress :one
SELECT * FROM profiles
WHERE address = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: GetChallengeBySessionID :one
SELECT challenge FROM sessions
WHERE id = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: GetHumanVerificationNumbers :one
SELECT is_human_first, is_human_last FROM sessions
WHERE id = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: GetSessionByID :one
SELECT * FROM sessions
WHERE id = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: GetSessionByClientIP :one
SELECT * FROM sessions
WHERE client_ipaddr = $1 AND deleted_at IS NULL
LIMIT 1;
-- name: UpdateSessionHumanVerification :one
UPDATE sessions
SET
is_human_first = $1,
is_human_last = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3
RETURNING *;
-- name: UpdateSessionWithProfileID :one
UPDATE sessions
SET
profile_id = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2
RETURNING *;
-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = $1
AND deleted_at IS NULL;
-- name: GetCredentialsByHandle :many
SELECT * FROM credentials
WHERE handle = $1
AND deleted_at IS NULL;
-- name: GetCredentialByID :one
SELECT * FROM credentials
WHERE credential_id = $1
AND deleted_at IS NULL
LIMIT 1;
-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
WHERE credential_id = $1;
-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = $1;
-- name: UpdateProfile :one
UPDATE profiles
SET
name = $1,
handle = $2,
updated_at = CURRENT_TIMESTAMP
WHERE address = $3
AND deleted_at IS NULL
RETURNING *;
-- name: GetProfileByHandle :one
SELECT * FROM profiles
WHERE handle = $1
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultConfigByCID :one
SELECT * FROM vaults
WHERE cid = $1
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = $1
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 ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING *;
+121
View File
@@ -0,0 +1,121 @@
-- Profiles represent user identities
CREATE TABLE profiles (
id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
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 TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
number BIGINT NOT NULL,
sequence INT 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,
is_validator BOOLEAN NOT NULL DEFAULT FALSE,
is_delegator BOOLEAN NOT NULL DEFAULT FALSE,
is_accountable BOOLEAN NOT NULL DEFAULT TRUE
);
-- Assets represent tokens and coins
CREATE TABLE assets (
id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
decimals INT 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 TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
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 TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
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,
is_mobile BOOLEAN NOT NULL DEFAULT FALSE,
is_tablet BOOLEAN NOT NULL DEFAULT FALSE,
is_tv BOOLEAN NOT NULL DEFAULT FALSE,
is_bot BOOLEAN NOT NULL DEFAULT FALSE,
challenge TEXT NOT NULL,
is_human_first BOOLEAN NOT NULL DEFAULT FALSE,
is_human_last BOOLEAN NOT NULL DEFAULT FALSE,
profile_id TEXT NOT NULL REFERENCES profiles(id),
FOREIGN KEY (profile_id) REFERENCES profiles(id)
);
-- Vaults store encrypted data
CREATE TABLE vaults (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
handle TEXT NOT NULL,
origin TEXT NOT NULL,
address TEXT NOT NULL,
cid TEXT NOT NULL UNIQUE,
config JSONB NOT NULL,
session_id BIGINT NOT NULL,
redirect_uri TEXT NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- 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);
@@ -1,8 +1,8 @@
package sink
package sqlite
import (
_ "embed"
)
//go:embed schema.sql
var SchemaSQL string
var SchemaMotrSQL string
+19
View File
@@ -0,0 +1,19 @@
version: "2"
sql:
- engine: "sqlite"
queries: "./sink/sqlite/query.sql"
schema: "./sink/sqlite/schema.sql"
gen:
go:
package: "motrorm"
out: "drivers/motrorm"
- engine: "postgresql"
queries: "./sink/postgres/query.sql"
schema: "./sink/postgres/schema.sql"
gen:
go:
package: "hwayorm"
out: "drivers/hwayorm"
sql_package: "pgx/v5"
+2 -2
View File
@@ -23,8 +23,8 @@ templ Handle() {
<br/>
}
templ HandleError(value string) {
<sl-input name="handle" placeholder="digitalgold" type="text" label="Handle" minlength="4" maxlength="12" required class="border-red-500" value={ value }>
templ HandleError(value string, helpText string) {
<sl-input name="handle" placeholder="digitalgold" type="text" label="Handle" minlength="4" maxlength="12" required class="border-red-500" value={ value } help-text={ helpText }>
<div slot="prefix">
<sl-icon name="at-sign" library="sonr"></sl-icon>
</div>
+20 -7
View File
@@ -49,7 +49,7 @@ func Handle() templ.Component {
})
}
func HandleError(value string) templ.Component {
func HandleError(value string, helpText string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -83,6 +83,19 @@ func HandleError(value string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" help-text=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(helpText)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/nebula/input/input_handle.templ`, Line: 27, Col: 175}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div><div slot=\"suffix\" style=\"color: #B54549;\"><sl-icon name=\"x\"></sl-icon></div></sl-input><br>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -107,21 +120,21 @@ func HandleSuccess(value string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
if templ_7745c5c3_Var4 == nil {
templ_7745c5c3_Var4 = templ.NopComponent
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-input name=\"handle\" placeholder=\"digitalgold\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required class=\"border-green-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(value)
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/nebula/input/input_handle.templ`, Line: 39, Col: 154}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}