refactor: restructure application layout and dependencies for improved modularity

This commit is contained in:
2025-04-03 17:45:18 -04:00
parent f9e307c796
commit a07f13bf93
24 changed files with 7 additions and 1798 deletions

View File

@@ -19,8 +19,8 @@ import (
_ "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
vault "github.com/onsonr/motr/app"
"github.com/onsonr/motr/pkg/models"
sink "github.com/onsonr/motr/pkg/sink"
"github.com/onsonr/motr/internal/models"
sink "github.com/onsonr/motr/internal/sink"
)
var (

View File

@@ -1,142 +0,0 @@
package embed
import (
"encoding/json"
"reflect"
"strings"
_ "embed"
"github.com/onsonr/motr/pkg/types"
)
//go:embed index.html
var IndexHTML []byte
//go:embed main.js
var MainJS []byte
//go:embed sw.js
var WorkerJS []byte
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) (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) *types.Config {
return &types.Config{
MotrToken: ucanCID,
MotrAddress: addr,
IpfsGatewayURL: "http://localhost:80",
SonrAPIURL: "http://localhost:1317",
SonrRPCURL: "http://localhost:26657",
SonrChainID: "sonr-testnet-1",
}
}
func NewWebManifest() ([]byte, error) {
return json.Marshal(baseWebManifest)
}
var baseWebManifest = types.WebManifest{
Name: "Sonr Vault",
ShortName: "Sonr.ID",
StartURL: "/index.html",
Display: "standalone",
DisplayOverride: []string{
"fullscreen",
"minimal-ui",
},
Icons: []types.IconDefinition{
{
Src: "/icons/icon-192x192.png",
Sizes: "192x192",
Type: "image/png",
},
},
ServiceWorker: types.ServiceWorker{
Scope: "/",
Src: "/sw.js",
UseCache: true,
},
ProtocolHandlers: []types.ProtocolHandler{
{
Scheme: "did.sonr",
URL: "/resolve/sonr/%s",
},
{
Scheme: "did.eth",
URL: "/resolve/eth/%s",
},
{
Scheme: "did.btc",
URL: "/resolve/btc/%s",
},
{
Scheme: "did.usdc",
URL: "/resolve/usdc/%s",
},
{
Scheme: "did.ipfs",
URL: "/resolve/ipfs/%s",
},
},
}
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:]
}

View File

@@ -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>

View File

@@ -1,158 +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}`);
},
};

View File

@@ -1,257 +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());
}
});

View File

@@ -1,31 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package models
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -1,99 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package models
import (
"database/sql"
"time"
)
type Account struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Number int64 `json:"number"`
Sequence int64 `json:"sequence"`
Address string `json:"address"`
PublicKey string `json:"public_key"`
ChainID string `json:"chain_id"`
Controller string `json:"controller"`
IsSubsidiary bool `json:"is_subsidiary"`
IsValidator bool `json:"is_validator"`
IsDelegator bool `json:"is_delegator"`
IsAccountable bool `json:"is_accountable"`
}
type Asset struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int64 `json:"decimals"`
ChainID string `json:"chain_id"`
Channel string `json:"channel"`
AssetType string `json:"asset_type"`
CoingeckoID sql.NullString `json:"coingecko_id"`
}
type Credential struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
CredentialID string `json:"credential_id"`
AuthenticatorAttachment string `json:"authenticator_attachment"`
Origin string `json:"origin"`
Type string `json:"type"`
Transports string `json:"transports"`
}
type Profile struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Address string `json:"address"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Name string `json:"name"`
}
type Session struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
BrowserName string `json:"browser_name"`
BrowserVersion string `json:"browser_version"`
ClientIpaddr string `json:"client_ipaddr"`
Platform string `json:"platform"`
IsDesktop bool `json:"is_desktop"`
IsMobile bool `json:"is_mobile"`
IsTablet bool `json:"is_tablet"`
IsTv bool `json:"is_tv"`
IsBot bool `json:"is_bot"`
Challenge string `json:"challenge"`
IsHumanFirst bool `json:"is_human_first"`
IsHumanLast bool `json:"is_human_last"`
ProfileID int64 `json:"profile_id"`
}
type Vault struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Address string `json:"address"`
Cid string `json:"cid"`
Config string `json:"config"`
SessionID string `json:"session_id"`
RedirectUri string `json:"redirect_uri"`
}

View File

@@ -1,34 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package models
import (
"context"
)
type Querier interface {
CheckHandleExists(ctx context.Context, handle string) (bool, error)
CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error)
GetChallengeBySessionID(ctx context.Context, id string) (string, error)
GetCredentialByID(ctx context.Context, credentialID string) (Credential, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error)
GetHumanVerificationNumbers(ctx context.Context, id string) (GetHumanVerificationNumbersRow, error)
GetProfileByAddress(ctx context.Context, address string) (Profile, error)
GetProfileByHandle(ctx context.Context, handle string) (Profile, error)
GetProfileByID(ctx context.Context, id string) (Profile, error)
GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error)
GetSessionByID(ctx context.Context, id string) (Session, error)
GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error)
GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error)
InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error)
InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error)
SoftDeleteCredential(ctx context.Context, credentialID string) error
SoftDeleteProfile(ctx context.Context, address string) error
UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error)
UpdateSessionHumanVerification(ctx context.Context, arg UpdateSessionHumanVerificationParams) (Session, error)
UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -1,581 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
// source: query.sql
package models
import (
"context"
)
const checkHandleExists = `-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = ?
AND deleted_at IS NULL
`
func (q *Queries) CheckHandleExists(ctx context.Context, handle string) (bool, error) {
row := q.db.QueryRowContext(ctx, checkHandleExists, handle)
var handle_exists bool
err := row.Scan(&handle_exists)
return handle_exists, err
}
const createSession = `-- name: CreateSession :one
INSERT INTO sessions (
id,
browser_name,
browser_version,
client_ipaddr,
platform,
is_desktop,
is_mobile,
is_tablet,
is_tv,
is_bot,
challenge,
is_human_first,
is_human_last,
profile_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
`
type CreateSessionParams struct {
ID string `json:"id"`
BrowserName string `json:"browser_name"`
BrowserVersion string `json:"browser_version"`
ClientIpaddr string `json:"client_ipaddr"`
Platform string `json:"platform"`
IsDesktop bool `json:"is_desktop"`
IsMobile bool `json:"is_mobile"`
IsTablet bool `json:"is_tablet"`
IsTv bool `json:"is_tv"`
IsBot bool `json:"is_bot"`
Challenge string `json:"challenge"`
IsHumanFirst bool `json:"is_human_first"`
IsHumanLast bool `json:"is_human_last"`
ProfileID int64 `json:"profile_id"`
}
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
row := q.db.QueryRowContext(ctx, createSession,
arg.ID,
arg.BrowserName,
arg.BrowserVersion,
arg.ClientIpaddr,
arg.Platform,
arg.IsDesktop,
arg.IsMobile,
arg.IsTablet,
arg.IsTv,
arg.IsBot,
arg.Challenge,
arg.IsHumanFirst,
arg.IsHumanLast,
arg.ProfileID,
)
var i Session
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.BrowserName,
&i.BrowserVersion,
&i.ClientIpaddr,
&i.Platform,
&i.IsDesktop,
&i.IsMobile,
&i.IsTablet,
&i.IsTv,
&i.IsBot,
&i.Challenge,
&i.IsHumanFirst,
&i.IsHumanLast,
&i.ProfileID,
)
return i, err
}
const getChallengeBySessionID = `-- name: GetChallengeBySessionID :one
SELECT challenge FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetChallengeBySessionID(ctx context.Context, id string) (string, error) {
row := q.db.QueryRowContext(ctx, getChallengeBySessionID, id)
var challenge string
err := row.Scan(&challenge)
return challenge, err
}
const getCredentialByID = `-- name: GetCredentialByID :one
SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials
WHERE credential_id = ?
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetCredentialByID(ctx context.Context, credentialID string) (Credential, error) {
row := q.db.QueryRowContext(ctx, getCredentialByID, credentialID)
var i Credential
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.CredentialID,
&i.AuthenticatorAttachment,
&i.Origin,
&i.Type,
&i.Transports,
)
return i, err
}
const getCredentialsByHandle = `-- name: GetCredentialsByHandle :many
SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials
WHERE handle = ?
AND deleted_at IS NULL
`
func (q *Queries) GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error) {
rows, err := q.db.QueryContext(ctx, getCredentialsByHandle, handle)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Credential
for rows.Next() {
var i Credential
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.CredentialID,
&i.AuthenticatorAttachment,
&i.Origin,
&i.Type,
&i.Transports,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getHumanVerificationNumbers = `-- name: GetHumanVerificationNumbers :one
SELECT is_human_first, is_human_last FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
type GetHumanVerificationNumbersRow struct {
IsHumanFirst bool `json:"is_human_first"`
IsHumanLast bool `json:"is_human_last"`
}
func (q *Queries) GetHumanVerificationNumbers(ctx context.Context, id string) (GetHumanVerificationNumbersRow, error) {
row := q.db.QueryRowContext(ctx, getHumanVerificationNumbers, id)
var i GetHumanVerificationNumbersRow
err := row.Scan(&i.IsHumanFirst, &i.IsHumanLast)
return i, err
}
const getProfileByAddress = `-- name: GetProfileByAddress :one
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
WHERE address = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByAddress(ctx context.Context, address string) (Profile, error) {
row := q.db.QueryRowContext(ctx, getProfileByAddress, address)
var i Profile
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Address,
&i.Handle,
&i.Origin,
&i.Name,
)
return i, err
}
const getProfileByHandle = `-- name: GetProfileByHandle :one
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
WHERE handle = ?
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByHandle(ctx context.Context, handle string) (Profile, error) {
row := q.db.QueryRowContext(ctx, getProfileByHandle, handle)
var i Profile
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Address,
&i.Handle,
&i.Origin,
&i.Name,
)
return i, err
}
const getProfileByID = `-- name: GetProfileByID :one
SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetProfileByID(ctx context.Context, id string) (Profile, error) {
row := q.db.QueryRowContext(ctx, getProfileByID, id)
var i Profile
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Address,
&i.Handle,
&i.Origin,
&i.Name,
)
return i, err
}
const getSessionByClientIP = `-- name: GetSessionByClientIP :one
SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions
WHERE client_ipaddr = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error) {
row := q.db.QueryRowContext(ctx, getSessionByClientIP, clientIpaddr)
var i Session
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.BrowserName,
&i.BrowserVersion,
&i.ClientIpaddr,
&i.Platform,
&i.IsDesktop,
&i.IsMobile,
&i.IsTablet,
&i.IsTv,
&i.IsBot,
&i.Challenge,
&i.IsHumanFirst,
&i.IsHumanLast,
&i.ProfileID,
)
return i, err
}
const getSessionByID = `-- name: GetSessionByID :one
SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error) {
row := q.db.QueryRowContext(ctx, getSessionByID, id)
var i Session
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.BrowserName,
&i.BrowserVersion,
&i.ClientIpaddr,
&i.Platform,
&i.IsDesktop,
&i.IsMobile,
&i.IsTablet,
&i.IsTv,
&i.IsBot,
&i.Challenge,
&i.IsHumanFirst,
&i.IsHumanLast,
&i.ProfileID,
)
return i, err
}
const getVaultConfigByCID = `-- name: GetVaultConfigByCID :one
SELECT id, created_at, updated_at, deleted_at, handle, origin, address, cid, config, session_id, redirect_uri FROM vaults
WHERE cid = ?
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error) {
row := q.db.QueryRowContext(ctx, getVaultConfigByCID, cid)
var i Vault
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.Origin,
&i.Address,
&i.Cid,
&i.Config,
&i.SessionID,
&i.RedirectUri,
)
return i, err
}
const getVaultRedirectURIBySessionID = `-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = ?
AND deleted_at IS NULL
LIMIT 1
`
func (q *Queries) GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error) {
row := q.db.QueryRowContext(ctx, getVaultRedirectURIBySessionID, sessionID)
var redirect_uri string
err := row.Scan(&redirect_uri)
return redirect_uri, err
}
const insertCredential = `-- name: InsertCredential :one
INSERT INTO credentials (
handle,
credential_id,
origin,
type,
transports
) VALUES (?, ?, ?, ?, ?)
RETURNING id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports
`
type InsertCredentialParams struct {
Handle string `json:"handle"`
CredentialID string `json:"credential_id"`
Origin string `json:"origin"`
Type string `json:"type"`
Transports string `json:"transports"`
}
func (q *Queries) InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error) {
row := q.db.QueryRowContext(ctx, insertCredential,
arg.Handle,
arg.CredentialID,
arg.Origin,
arg.Type,
arg.Transports,
)
var i Credential
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Handle,
&i.CredentialID,
&i.AuthenticatorAttachment,
&i.Origin,
&i.Type,
&i.Transports,
)
return i, err
}
const insertProfile = `-- name: InsertProfile :one
INSERT INTO profiles (
address,
handle,
origin,
name
) VALUES (?, ?, ?, ?)
RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name
`
type InsertProfileParams struct {
Address string `json:"address"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Name string `json:"name"`
}
func (q *Queries) InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error) {
row := q.db.QueryRowContext(ctx, insertProfile,
arg.Address,
arg.Handle,
arg.Origin,
arg.Name,
)
var i Profile
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Address,
&i.Handle,
&i.Origin,
&i.Name,
)
return i, err
}
const softDeleteCredential = `-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
WHERE credential_id = ?
`
func (q *Queries) SoftDeleteCredential(ctx context.Context, credentialID string) error {
_, err := q.db.ExecContext(ctx, softDeleteCredential, credentialID)
return err
}
const softDeleteProfile = `-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = ?
`
func (q *Queries) SoftDeleteProfile(ctx context.Context, address string) error {
_, err := q.db.ExecContext(ctx, softDeleteProfile, address)
return err
}
const updateProfile = `-- name: UpdateProfile :one
UPDATE profiles
SET
name = ?,
handle = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name
`
type UpdateProfileParams struct {
Name string `json:"name"`
Handle string `json:"handle"`
Address string `json:"address"`
}
func (q *Queries) UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error) {
row := q.db.QueryRowContext(ctx, updateProfile, arg.Name, arg.Handle, arg.Address)
var i Profile
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.Address,
&i.Handle,
&i.Origin,
&i.Name,
)
return i, err
}
const updateSessionHumanVerification = `-- name: UpdateSessionHumanVerification :one
UPDATE sessions
SET
is_human_first = ?,
is_human_last = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
`
type UpdateSessionHumanVerificationParams struct {
IsHumanFirst bool `json:"is_human_first"`
IsHumanLast bool `json:"is_human_last"`
ID string `json:"id"`
}
func (q *Queries) UpdateSessionHumanVerification(ctx context.Context, arg UpdateSessionHumanVerificationParams) (Session, error) {
row := q.db.QueryRowContext(ctx, updateSessionHumanVerification, arg.IsHumanFirst, arg.IsHumanLast, arg.ID)
var i Session
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.BrowserName,
&i.BrowserVersion,
&i.ClientIpaddr,
&i.Platform,
&i.IsDesktop,
&i.IsMobile,
&i.IsTablet,
&i.IsTv,
&i.IsBot,
&i.Challenge,
&i.IsHumanFirst,
&i.IsHumanLast,
&i.ProfileID,
)
return i, err
}
const updateSessionWithProfileID = `-- name: UpdateSessionWithProfileID :one
UPDATE sessions
SET
profile_id = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id
`
type UpdateSessionWithProfileIDParams struct {
ProfileID int64 `json:"profile_id"`
ID string `json:"id"`
}
func (q *Queries) UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error) {
row := q.db.QueryRowContext(ctx, updateSessionWithProfileID, arg.ProfileID, arg.ID)
var i Session
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.DeletedAt,
&i.BrowserName,
&i.BrowserVersion,
&i.ClientIpaddr,
&i.Platform,
&i.IsDesktop,
&i.IsMobile,
&i.IsTablet,
&i.IsTv,
&i.IsBot,
&i.Challenge,
&i.IsHumanFirst,
&i.IsHumanLast,
&i.ProfileID,
)
return i, err
}

View File

@@ -1,138 +0,0 @@
-- name: InsertCredential :one
INSERT INTO credentials (
handle,
credential_id,
origin,
type,
transports
) VALUES (?, ?, ?, ?, ?)
RETURNING *;
-- name: InsertProfile :one
INSERT INTO profiles (
address,
handle,
origin,
name
) VALUES (?, ?, ?, ?)
RETURNING *;
-- name: GetProfileByID :one
SELECT * FROM profiles
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByAddress :one
SELECT * FROM profiles
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetChallengeBySessionID :one
SELECT challenge FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetHumanVerificationNumbers :one
SELECT is_human_first, is_human_last FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetSessionByID :one
SELECT * FROM sessions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetSessionByClientIP :one
SELECT * FROM sessions
WHERE client_ipaddr = ? AND deleted_at IS NULL
LIMIT 1;
-- name: UpdateSessionHumanVerification :one
UPDATE sessions
SET
is_human_first = ?,
is_human_last = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
RETURNING *;
-- name: UpdateSessionWithProfileID :one
UPDATE sessions
SET
profile_id = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
RETURNING *;
-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = ?
AND deleted_at IS NULL;
-- name: GetCredentialsByHandle :many
SELECT * FROM credentials
WHERE handle = ?
AND deleted_at IS NULL;
-- name: GetCredentialByID :one
SELECT * FROM credentials
WHERE credential_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
WHERE credential_id = ?;
-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = ?;
-- name: UpdateProfile :one
UPDATE profiles
SET
name = ?,
handle = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING *;
-- name: GetProfileByHandle :one
SELECT * FROM profiles
WHERE handle = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultConfigByCID :one
SELECT * FROM vaults
WHERE cid = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: CreateSession :one
INSERT INTO sessions (
id,
browser_name,
browser_version,
client_ipaddr,
platform,
is_desktop,
is_mobile,
is_tablet,
is_tv,
is_bot,
challenge,
is_human_first,
is_human_last,
profile_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
RETURNING *;

View File

@@ -1,121 +0,0 @@
-- Profiles represent user identities
CREATE TABLE profiles (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
address TEXT NOT NULL,
handle TEXT NOT NULL UNIQUE,
origin TEXT NOT NULL,
name TEXT NOT NULL,
UNIQUE(address, origin)
);
-- Accounts represent blockchain accounts
CREATE TABLE accounts (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
number INTEGER NOT NULL,
sequence INTEGER NOT NULL DEFAULT 0,
address TEXT NOT NULL UNIQUE,
public_key TEXT NOT NULL CHECK(json_valid(public_key)),
chain_id TEXT NOT NULL,
controller TEXT NOT NULL,
is_subsidiary BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_subsidiary IN (0,1)),
is_validator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_validator IN (0,1)),
is_delegator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegator IN (0,1)),
is_accountable BOOLEAN NOT NULL DEFAULT TRUE CHECK(is_accountable IN (0,1))
);
-- Assets represent tokens and coins
CREATE TABLE assets (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
decimals INTEGER NOT NULL CHECK(decimals >= 0),
chain_id TEXT NOT NULL,
channel TEXT NOT NULL,
asset_type TEXT NOT NULL,
coingecko_id TEXT,
UNIQUE(chain_id, symbol)
);
-- Credentials store WebAuthn credentials
CREATE TABLE credentials (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
handle TEXT NOT NULL,
credential_id TEXT NOT NULL UNIQUE,
authenticator_attachment TEXT NOT NULL,
origin TEXT NOT NULL,
type TEXT NOT NULL,
transports TEXT NOT NULL
);
-- Sessions track user authentication state
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
browser_name TEXT NOT NULL,
browser_version TEXT NOT NULL,
client_ipaddr TEXT NOT NULL,
platform TEXT NOT NULL,
is_desktop BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_desktop IN (0,1)),
is_mobile BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_mobile IN (0,1)),
is_tablet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tablet IN (0,1)),
is_tv BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tv IN (0,1)),
is_bot BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_bot IN (0,1)),
challenge TEXT NOT NULL,
is_human_first BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_first IN (0,1)),
is_human_last BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_last IN (0,1)),
profile_id INTEGER NOT NULL
);
-- Vaults store encrypted data
CREATE TABLE vaults (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
handle TEXT NOT NULL,
origin TEXT NOT NULL,
address TEXT NOT NULL,
cid TEXT NOT NULL UNIQUE,
config TEXT NOT NULL CHECK(json_valid(config)),
session_id TEXT NOT NULL,
redirect_uri TEXT NOT NULL
);
-- Indexes for common queries
CREATE INDEX idx_profiles_handle ON profiles(handle);
CREATE INDEX idx_profiles_address ON profiles(address);
CREATE INDEX idx_profiles_deleted_at ON profiles(deleted_at);
CREATE INDEX idx_accounts_address ON accounts(address);
CREATE INDEX idx_accounts_chain_id ON accounts(chain_id);
CREATE INDEX idx_accounts_deleted_at ON accounts(deleted_at);
CREATE INDEX idx_assets_symbol ON assets(symbol);
CREATE INDEX idx_assets_chain_id ON assets(chain_id);
CREATE INDEX idx_assets_deleted_at ON assets(deleted_at);
CREATE INDEX idx_credentials_handle ON credentials(handle);
CREATE INDEX idx_credentials_origin ON credentials(origin);
CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at);
CREATE INDEX idx_sessions_profile_id ON sessions(profile_id);
CREATE INDEX idx_sessions_client_ipaddr ON sessions(client_ipaddr);
CREATE INDEX idx_sessions_deleted_at ON sessions(deleted_at);
CREATE INDEX idx_vaults_handle ON vaults(handle);
CREATE INDEX idx_vaults_session_id ON vaults(session_id);
CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at);

View File

@@ -1,8 +0,0 @@
package sink
import (
_ "embed"
)
//go:embed schema.sql
var SchemaVaultSQL string

View File

@@ -1,11 +0,0 @@
package types
type Config struct {
// TODO
MotrToken string `json:"motr_token"`
MotrAddress string `json:"motr_address"`
IpfsGatewayURL string `json:"ipfs_gateway_url"`
SonrAPIURL string `json:"sonr_api_url"`
SonrRPCURL string `json:"sonr_rpc_url"`
SonrChainID string `json:"sonr_chain_id"`
}

View File

@@ -1,73 +0,0 @@
package types
type WebManifest struct {
// Required fields
Name string `json:"name"` // Full name of the application
ShortName string `json:"short_name"` // Short version of the name
// Display and appearance
Description string `json:"description,omitempty"` // Purpose and features of the application
Display string `json:"display,omitempty"` // Preferred display mode: fullscreen, standalone, minimal-ui, browser
DisplayOverride []string `json:"display_override,omitempty"`
ThemeColor string `json:"theme_color,omitempty"` // Default theme color for the application
BackgroundColor string `json:"background_color,omitempty"` // Background color during launch
Orientation string `json:"orientation,omitempty"` // Default orientation: any, natural, landscape, portrait
// URLs and scope
StartURL string `json:"start_url"` // Starting URL when launching
Scope string `json:"scope,omitempty"` // Navigation scope of the web application
ServiceWorker ServiceWorker `json:"service_worker,omitempty"`
// Icons
Icons []IconDefinition `json:"icons,omitempty"`
// Optional features
RelatedApplications []RelatedApplication `json:"related_applications,omitempty"`
PreferRelatedApplications bool `json:"prefer_related_applications,omitempty"`
Shortcuts []Shortcut `json:"shortcuts,omitempty"`
// Experimental features (uncomment if needed)
FileHandlers []FileHandler `json:"file_handlers,omitempty"`
ProtocolHandlers []ProtocolHandler `json:"protocol_handlers,omitempty"`
}
type FileHandler struct {
Action string `json:"action"`
Accept map[string][]string `json:"accept"`
}
type LaunchHandler struct {
Action string `json:"action"`
}
type IconDefinition struct {
Src string `json:"src"`
Sizes string `json:"sizes"`
Type string `json:"type,omitempty"`
Purpose string `json:"purpose,omitempty"`
}
type ProtocolHandler struct {
Scheme string `json:"scheme"`
URL string `json:"url"`
}
type RelatedApplication struct {
Platform string `json:"platform"`
URL string `json:"url,omitempty"`
ID string `json:"id,omitempty"`
}
type Shortcut struct {
Name string `json:"name"`
ShortName string `json:"short_name,omitempty"`
Description string `json:"description,omitempty"`
URL string `json:"url"`
Icons []IconDefinition `json:"icons,omitempty"`
}
type ServiceWorker struct {
Scope string `json:"scope"`
Src string `json:"src"`
UseCache bool `json:"use_cache"`
}

View File

@@ -6,8 +6,8 @@ import (
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"github.com/onsonr/motr/internal/context"
"github.com/onsonr/motr/pkg/models"
"github.com/onsonr/motr/pkg/types"
"github.com/onsonr/motr/internal/models"
"github.com/onsonr/motr/types"
)
type Vault = *echo.Echo

View File

@@ -1,11 +1,11 @@
version: "2"
sql:
- engine: "sqlite"
queries: "./pkg/sink/query.sql"
schema: "./pkg/sink/schema.sql"
queries: "./internal/sink/query.sql"
schema: "./internal/sink/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "models"
out: "./pkg/models"
out: "./internal/models"