refactor: migrate config package to pkg directory

This commit is contained in:
Prad Nukala
2024-12-11 12:24:04 -05:00
parent 0502f52ec0
commit d667c3c604
11 changed files with 9 additions and 9 deletions
+3 -3
View File
@@ -4,13 +4,13 @@ import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gateway/config"
config "github.com/onsonr/sonr/pkg/config/hway"
"github.com/onsonr/sonr/pkg/database/sessions"
"gorm.io/gorm"
)
// Middleware creates a new session middleware
func Middleware(db *gorm.DB, env config.Env) echo.MiddlewareFunc {
func Middleware(db *gorm.DB, env config.Hway) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cc := NewHTTPContext(c, db)
@@ -27,7 +27,7 @@ type HTTPContext struct {
echo.Context
db *gorm.DB
sess *sessions.Session
env config.Env
env config.Hway
}
// Get returns the HTTPContext from the echo context
+2 -2
View File
@@ -3,15 +3,15 @@ package gateway
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gateway/config"
"github.com/onsonr/sonr/internal/gateway/context"
"github.com/onsonr/sonr/internal/gateway/handlers/index"
"github.com/onsonr/sonr/internal/gateway/handlers/register"
"github.com/onsonr/sonr/pkg/common/response"
config "github.com/onsonr/sonr/pkg/config/hway"
"gorm.io/gorm"
)
func RegisterRoutes(e *echo.Echo, env config.Env, db *gorm.DB) error {
func RegisterRoutes(e *echo.Echo, env config.Hway, db *gorm.DB) error {
// Custom error handler for gateway
e.HTTPErrorHandler = response.RedirectOnError("http://localhost:3000")
-49
View File
@@ -1,49 +0,0 @@
package embed
import (
"encoding/json"
"github.com/ipfs/boxo/files"
config "github.com/onsonr/sonr/pkg/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 *config.Config) (files.Directory, error) {
manifestBz, err := newWebManifestBytes()
if err != nil {
return nil, err
}
cnfBz, err := json.Marshal(cfg)
if err != nil {
return nil, err
}
return files.NewMapDirectory(map[string]files.Node{
AppManifestFileName: files.NewBytesFile(manifestBz),
DWNConfigFileName: files.NewBytesFile(cnfBz),
IndexHTMLFileName: files.NewBytesFile(IndexHTML),
MainJSFileName: files.NewBytesFile(MainJS),
ServiceWorkerFileName: files.NewBytesFile(WorkerJS),
}), nil
}
// NewVaultConfig returns the default vault config
func NewVaultConfig(addr string, ucanCID string) *config.Config {
return &config.Config{
MotrToken: ucanCID,
MotrAddress: addr,
IpfsGatewayUrl: "http://localhost:80",
SonrApiUrl: "http://localhost:1317",
SonrRpcUrl: "http://localhost:26657",
SonrChainId: "sonr-testnet-1",
VaultSchema: DefaultSchema(),
}
}
-138
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>
-152
View File
@@ -1,152 +0,0 @@
// MessageChannel for WASM communication
let wasmChannel;
let wasmPort;
async function initWasmChannel() {
wasmChannel = new MessageChannel();
wasmPort = wasmChannel.port1;
// Setup message handling from WASM
wasmPort.onmessage = (event) => {
const { type, data } = event.data;
switch (type) {
case 'WASM_READY':
console.log('WASM is ready');
document.dispatchEvent(new CustomEvent('wasm-ready'));
break;
case 'RESPONSE':
handleWasmResponse(data);
break;
case 'SYNC_COMPLETE':
handleSyncComplete(data);
break;
}
};
}
// Initialize WebAssembly and Service Worker
async function init() {
try {
// Register service worker
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.register('./sw.js');
console.log('ServiceWorker registered');
// Wait for the service worker to be ready
await navigator.serviceWorker.ready;
// Initialize MessageChannel
await initWasmChannel();
// Send the MessageChannel port to the service worker
navigator.serviceWorker.controller.postMessage({
type: 'PORT_INITIALIZATION',
port: wasmChannel.port2
}, [wasmChannel.port2]);
// Register for periodic sync if available
if ('periodicSync' in registration) {
try {
await registration.periodicSync.register('wasm-sync', {
minInterval: 24 * 60 * 60 * 1000 // 24 hours
});
} catch (error) {
console.log('Periodic sync could not be registered:', error);
}
}
}
// Initialize HTMX with custom config
htmx.config.withCredentials = true;
htmx.config.wsReconnectDelay = 'full-jitter';
// Override HTMX's internal request handling
htmx.config.beforeRequest = function (config) {
// Add request ID for tracking
const requestId = 'req_' + Date.now();
config.headers['X-Wasm-Request-ID'] = requestId;
// If offline, handle through service worker
if (!navigator.onLine) {
return false; // Let service worker handle it
}
return true;
};
// Handle HTMX after request
htmx.config.afterRequest = function (config) {
// Additional processing after request if needed
};
// Handle HTMX errors
htmx.config.errorHandler = function (error) {
console.error('HTMX Error:', error);
};
} catch (error) {
console.error('Initialization failed:', error);
}
}
function handleWasmResponse(data) {
const { requestId, response } = data;
// Process the WASM response
// This might update the UI or trigger HTMX swaps
const targetElement = document.querySelector(`[data-request-id="${requestId}"]`);
if (targetElement) {
htmx.process(targetElement);
}
}
function handleSyncComplete(data) {
const { url } = data;
// Handle successful sync
// Maybe refresh the relevant part of the UI
htmx.trigger('body', 'sync:complete', { url });
}
// Handle offline status changes
window.addEventListener('online', () => {
document.body.classList.remove('offline');
// Trigger sync when back online
if (wasmPort) {
wasmPort.postMessage({ type: 'SYNC_REQUEST' });
}
});
window.addEventListener('offline', () => {
document.body.classList.add('offline');
});
// Custom event handlers for HTMX
document.addEventListener('htmx:beforeRequest', (event) => {
const { elt, xhr } = event.detail;
// Add request tracking
const requestId = xhr.headers['X-Wasm-Request-ID'];
elt.setAttribute('data-request-id', requestId);
});
document.addEventListener('htmx:afterRequest', (event) => {
const { elt, successful } = event.detail;
if (successful) {
elt.removeAttribute('data-request-id');
}
});
// Initialize everything when the page loads
document.addEventListener('DOMContentLoaded', init);
// Export functions that might be needed by WASM
window.wasmBridge = {
triggerUIUpdate: function (selector, content) {
const target = document.querySelector(selector);
if (target) {
htmx.process(htmx.parse(content).forEach(node => target.appendChild(node)));
}
},
showNotification: function (message, type = 'info') {
// Implement notification system
console.log(`${type}: ${message}`);
}
};
-124
View File
@@ -1,124 +0,0 @@
package embed
import "encoding/json"
func newWebManifestBytes() ([]byte, error) {
return json.Marshal(baseWebManifest)
}
var baseWebManifest = WebManifest{
Name: "Sonr Vault",
ShortName: "Sonr.ID",
StartURL: "/index.html",
Display: "standalone",
DisplayOverride: []string{
"fullscreen",
"minimal-ui",
},
Icons: []IconDefinition{
{
Src: "/icons/icon-192x192.png",
Sizes: "192x192",
Type: "image/png",
},
},
ServiceWorker: ServiceWorker{
Scope: "/",
Src: "/sw.js",
UseCache: true,
},
ProtocolHandlers: []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",
},
},
}
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"`
}
-54
View File
@@ -1,54 +0,0 @@
package embed
import (
"reflect"
"strings"
"github.com/onsonr/sonr/pkg/common/models"
config "github.com/onsonr/sonr/pkg/config/motr"
)
// DefaultSchema returns the default schema
func DefaultSchema() *config.Schema {
return &config.Schema{
Version: SchemaVersion,
Account: getSchema(&models.Account{}),
Asset: getSchema(&models.Asset{}),
Chain: getSchema(&models.Chain{}),
Credential: getSchema(&models.Credential{}),
Grant: getSchema(&models.Grant{}),
Keyshare: getSchema(&models.Keyshare{}),
Profile: getSchema(&models.Profile{}),
}
}
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:]
}
-14
View File
@@ -1,14 +0,0 @@
package embed
import (
_ "embed"
)
//go:embed index.html
var IndexHTML []byte
//go:embed main.js
var MainJS []byte
//go:embed sw.js
var WorkerJS []byte
-258
View File
@@ -1,258 +0,0 @@
// Cache names for different types of resources
const CACHE_NAMES = {
wasm: 'wasm-cache-v1',
static: 'static-cache-v1',
dynamic: 'dynamic-cache-v1'
};
// Import required scripts
importScripts(
"https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js",
"https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v1.1.0/sw.js",
);
// Initialize WASM HTTP listener
const wasmInstance = registerWasmHTTPListener("https://cdn.sonr.id/wasm/app.wasm");
// MessageChannel port for WASM communication
let wasmPort;
// Request queue for offline operations
let requestQueue = new Map();
// Setup message channel handler
self.addEventListener('message', async (event) => {
if (event.data.type === 'PORT_INITIALIZATION') {
wasmPort = event.data.port;
setupWasmCommunication();
}
});
function setupWasmCommunication() {
wasmPort.onmessage = async (event) => {
const { type, data } = event.data;
switch (type) {
case 'WASM_REQUEST':
handleWasmRequest(data);
break;
case 'SYNC_REQUEST':
processSyncQueue();
break;
}
};
// Notify that WASM is ready
wasmPort.postMessage({ type: 'WASM_READY' });
}
// Enhanced install event
self.addEventListener("install", (event) => {
event.waitUntil(
Promise.all([
skipWaiting(),
// Cache WASM binary and essential resources
caches.open(CACHE_NAMES.wasm).then(cache =>
cache.addAll([
'https://cdn.sonr.id/wasm/app.wasm',
'https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js'
])
)
])
);
});
// Enhanced activate event
self.addEventListener("activate", (event) => {
event.waitUntil(
Promise.all([
clients.claim(),
// Clean up old caches
caches.keys().then(keys =>
Promise.all(
keys.map(key => {
if (!Object.values(CACHE_NAMES).includes(key)) {
return caches.delete(key);
}
})
)
)
])
);
});
// Intercept fetch events
self.addEventListener('fetch', (event) => {
const request = event.request;
// Handle API requests differently from static resources
if (request.url.includes('/api/')) {
event.respondWith(handleApiRequest(request));
} else {
event.respondWith(handleStaticRequest(request));
}
});
async function handleApiRequest(request) {
try {
// Try to make the request
const response = await fetch(request.clone());
// If successful, pass through WASM handler
if (response.ok) {
return await processWasmResponse(request, response);
}
// If offline or failed, queue the request
await queueRequest(request);
// Return cached response if available
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
// Return offline response
return new Response(
JSON.stringify({ error: 'Currently offline' }),
{
status: 503,
headers: { 'Content-Type': 'application/json' }
}
);
} catch (error) {
await queueRequest(request);
return new Response(
JSON.stringify({ error: 'Request failed' }),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
);
}
}
async function handleStaticRequest(request) {
// Check cache first
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const response = await fetch(request);
// Cache successful responses
if (response.ok) {
const cache = await caches.open(CACHE_NAMES.static);
cache.put(request, response.clone());
}
return response;
} catch (error) {
// Return offline page for navigation requests
if (request.mode === 'navigate') {
return caches.match('/offline.html');
}
throw error;
}
}
async function processWasmResponse(request, response) {
// Clone the response before processing
const responseClone = response.clone();
try {
// Process through WASM
const processedResponse = await wasmInstance.processResponse(responseClone);
// Notify client through message channel
if (wasmPort) {
wasmPort.postMessage({
type: 'RESPONSE',
requestId: request.headers.get('X-Wasm-Request-ID'),
response: processedResponse
});
}
return processedResponse;
} catch (error) {
console.error('WASM processing error:', error);
return response;
}
}
async function queueRequest(request) {
const serializedRequest = await serializeRequest(request);
requestQueue.set(request.url, serializedRequest);
// Register for background sync
try {
await self.registration.sync.register('wasm-sync');
} catch (error) {
console.error('Sync registration failed:', error);
}
}
async function serializeRequest(request) {
const headers = {};
for (const [key, value] of request.headers.entries()) {
headers[key] = value;
}
return {
url: request.url,
method: request.method,
headers,
body: await request.text(),
timestamp: Date.now()
};
}
// Handle background sync
self.addEventListener('sync', (event) => {
if (event.tag === 'wasm-sync') {
event.waitUntil(processSyncQueue());
}
});
async function processSyncQueue() {
const requests = Array.from(requestQueue.values());
for (const serializedRequest of requests) {
try {
const response = await fetch(new Request(serializedRequest.url, {
method: serializedRequest.method,
headers: serializedRequest.headers,
body: serializedRequest.body
}));
if (response.ok) {
requestQueue.delete(serializedRequest.url);
// Notify client of successful sync
if (wasmPort) {
wasmPort.postMessage({
type: 'SYNC_COMPLETE',
url: serializedRequest.url
});
}
}
} catch (error) {
console.error('Sync failed for request:', error);
}
}
}
// Handle payment requests
self.addEventListener("canmakepayment", function (e) {
e.respondWith(Promise.resolve(true));
});
// Handle periodic sync if available
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'wasm-sync') {
event.waitUntil(processSyncQueue());
}
});