mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 09:21:39 +00:00
536 lines
15 KiB
Go
536 lines
15 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/hibiken/asynq"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/sonr-io/sonr/bridge/tasks"
|
|
"github.com/sonr-io/sonr/crypto/mpc"
|
|
"github.com/sonr-io/sonr/types/ipfs"
|
|
)
|
|
|
|
// VaultHandlers holds all vault-related handlers and their dependencies
|
|
type VaultHandlers struct {
|
|
IPFSClient ipfs.IPFSClient
|
|
ConnectionManager *ConnectionManager
|
|
SSEManager *SSEManager
|
|
}
|
|
|
|
// NewVaultHandlers creates a new VaultHandlers instance
|
|
func NewVaultHandlers(
|
|
ipfsClient ipfs.IPFSClient,
|
|
connManager *ConnectionManager,
|
|
sseManager *SSEManager,
|
|
) *VaultHandlers {
|
|
return &VaultHandlers{
|
|
IPFSClient: ipfsClient,
|
|
ConnectionManager: connManager,
|
|
SSEManager: sseManager,
|
|
}
|
|
}
|
|
|
|
// GetQueueFromPriority determines the appropriate queue based on priority
|
|
func GetQueueFromPriority(priority string) string {
|
|
switch priority {
|
|
case "critical", "high":
|
|
return "critical"
|
|
case "low":
|
|
return "low"
|
|
default:
|
|
return "default"
|
|
}
|
|
}
|
|
|
|
// GenerateHandler handles vault generation requests
|
|
func (vh *VaultHandlers) GenerateHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
// Extract user info from JWT token
|
|
user := c.Get("user").(*jwt.Token)
|
|
claims := user.Claims.(jwt.MapClaims)
|
|
userID := claims["user_id"].(string)
|
|
|
|
var req struct {
|
|
UserID int `json:"user_id"`
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
task, err := tasks.NewUCANDIDTask(req.UserID)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
// Broadcast task status to WebSocket and SSE connections
|
|
go func() {
|
|
message := TaskStatusMessage{
|
|
TaskID: info.ID,
|
|
Status: "enqueued",
|
|
Time: time.Now(),
|
|
}
|
|
vh.ConnectionManager.BroadcastToTask(info.ID, message)
|
|
vh.SSEManager.BroadcastToSSE(info.ID, message)
|
|
|
|
// Simulate task progression for demonstration
|
|
// In a real implementation, this would be triggered by the actual task processors
|
|
time.Sleep(1 * time.Second)
|
|
processingMessage := TaskStatusMessage{
|
|
TaskID: info.ID,
|
|
Status: "processing",
|
|
Progress: 50,
|
|
Time: time.Now(),
|
|
}
|
|
vh.ConnectionManager.BroadcastToTask(info.ID, processingMessage)
|
|
vh.SSEManager.BroadcastToSSE(info.ID, processingMessage)
|
|
|
|
// Simulate completion
|
|
time.Sleep(2 * time.Second)
|
|
completedMessage := TaskStatusMessage{
|
|
TaskID: info.ID,
|
|
Status: "completed",
|
|
Progress: 100,
|
|
Time: time.Now(),
|
|
}
|
|
vh.ConnectionManager.BroadcastToTask(info.ID, completedMessage)
|
|
vh.SSEManager.BroadcastToSSE(info.ID, completedMessage)
|
|
}()
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
"user_id": userID,
|
|
})
|
|
}
|
|
}
|
|
|
|
// SignHandler handles vault signing requests
|
|
func (vh *VaultHandlers) SignHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
var req struct {
|
|
Message []byte `json:"message"`
|
|
Enclave *mpc.EnclaveData `json:"enclave,omitempty"` // Direct enclave data (fallback)
|
|
EnclaveCID string `json:"enclave_cid,omitempty"` // IPFS CID reference
|
|
Password []byte `json:"password,omitempty"` // Decryption password for IPFS stored data
|
|
UCANToken string `json:"ucan_token,omitempty"` // UCAN token for authorization
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
if len(req.Message) == 0 {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Message is required"})
|
|
}
|
|
|
|
// Handle enclave data - either direct or via IPFS CID
|
|
var enclave *mpc.EnclaveData
|
|
if req.EnclaveCID != "" {
|
|
// Retrieve enclave from IPFS
|
|
if vh.IPFSClient == nil {
|
|
return c.JSON(
|
|
http.StatusServiceUnavailable,
|
|
map[string]string{"error": "IPFS client not available"},
|
|
)
|
|
}
|
|
|
|
encryptedData, err := vh.IPFSClient.Get(req.EnclaveCID)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusNotFound,
|
|
map[string]string{"error": "Failed to retrieve enclave from IPFS"},
|
|
)
|
|
}
|
|
|
|
// Create temporary enclave for decryption
|
|
tempEnclave := &mpc.EnclaveData{}
|
|
decryptedData, err := tempEnclave.Decrypt(req.Password, encryptedData)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to decrypt enclave data"},
|
|
)
|
|
}
|
|
|
|
// Unmarshal decrypted data
|
|
enclave = &mpc.EnclaveData{}
|
|
if err := enclave.Unmarshal(decryptedData); err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to parse enclave data"},
|
|
)
|
|
}
|
|
} else if req.Enclave != nil {
|
|
// Use directly provided enclave data
|
|
enclave = req.Enclave
|
|
} else {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Either enclave or enclave_cid is required"})
|
|
}
|
|
|
|
task, err := tasks.NewUCANSignTask(
|
|
0,
|
|
req.Message,
|
|
) // TODO: Extract userID from JWT or context
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
})
|
|
}
|
|
}
|
|
|
|
// VerifyHandler handles vault verification requests
|
|
func (vh *VaultHandlers) VerifyHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
var req struct {
|
|
PublicKey []byte `json:"public_key"`
|
|
Message []byte `json:"message"`
|
|
Signature []byte `json:"signature"`
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
if len(req.PublicKey) == 0 || len(req.Message) == 0 || len(req.Signature) == 0 {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "PublicKey, message, and signature are required"},
|
|
)
|
|
}
|
|
|
|
task, err := tasks.NewUCANVerifyTask(
|
|
0,
|
|
req.Message,
|
|
req.Signature,
|
|
) // TODO: Extract userID from JWT or context
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
})
|
|
}
|
|
}
|
|
|
|
// ExportHandler handles vault export requests
|
|
func (vh *VaultHandlers) ExportHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
var req struct {
|
|
Enclave *mpc.EnclaveData `json:"enclave,omitempty"` // Direct enclave data
|
|
EnclaveCID string `json:"enclave_cid,omitempty"` // IPFS CID reference
|
|
Password []byte `json:"password"` // For encryption/decryption
|
|
StoreIPFS bool `json:"store_ipfs,omitempty"` // Whether to store result in IPFS
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
if len(req.Password) == 0 {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Password is required"})
|
|
}
|
|
|
|
// Handle enclave data - either direct or via IPFS CID
|
|
var enclave *mpc.EnclaveData
|
|
if req.EnclaveCID != "" {
|
|
// Retrieve enclave from IPFS
|
|
if vh.IPFSClient == nil {
|
|
return c.JSON(
|
|
http.StatusServiceUnavailable,
|
|
map[string]string{"error": "IPFS client not available"},
|
|
)
|
|
}
|
|
|
|
encryptedData, err := vh.IPFSClient.Get(req.EnclaveCID)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusNotFound,
|
|
map[string]string{"error": "Failed to retrieve enclave from IPFS"},
|
|
)
|
|
}
|
|
|
|
// Create temporary enclave for decryption
|
|
tempEnclave := &mpc.EnclaveData{}
|
|
decryptedData, err := tempEnclave.Decrypt(req.Password, encryptedData)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to decrypt enclave data"},
|
|
)
|
|
}
|
|
|
|
// Unmarshal decrypted data
|
|
enclave = &mpc.EnclaveData{}
|
|
if err := enclave.Unmarshal(decryptedData); err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to parse enclave data"},
|
|
)
|
|
}
|
|
} else if req.Enclave != nil {
|
|
// Use directly provided enclave data
|
|
enclave = req.Enclave
|
|
} else {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Either enclave or enclave_cid is required"})
|
|
}
|
|
|
|
// If store_ipfs is true, encrypt and store the enclave in IPFS first
|
|
if req.StoreIPFS && vh.IPFSClient != nil {
|
|
encryptedData, err := enclave.Encrypt(req.Password)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to encrypt enclave data"},
|
|
)
|
|
}
|
|
|
|
cid, err := vh.IPFSClient.Add(encryptedData)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to store enclave in IPFS"},
|
|
)
|
|
}
|
|
|
|
// Return the CID for future reference
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"cid": cid,
|
|
"status": "stored",
|
|
"message": "Enclave data encrypted and stored in IPFS",
|
|
})
|
|
}
|
|
|
|
// Export functionality replaced with UCAN token creation for data access
|
|
// Convert export operation to UCAN token generation with export permissions
|
|
attenuations := []map[string]any{
|
|
{
|
|
"can": []string{"export", "read"},
|
|
"with": "vault://exported-data",
|
|
},
|
|
}
|
|
task, err := tasks.NewUCANTokenTask(
|
|
0,
|
|
"did:sonr:export-recipient",
|
|
attenuations,
|
|
time.Now().Add(24*time.Hour).Unix(),
|
|
)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
})
|
|
}
|
|
}
|
|
|
|
// ImportHandler handles vault import requests
|
|
func (vh *VaultHandlers) ImportHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
var req struct {
|
|
CID string `json:"cid"`
|
|
Password []byte `json:"password"`
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
if req.CID == "" || len(req.Password) == 0 {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "CID and password are required"},
|
|
)
|
|
}
|
|
|
|
// Import functionality replaced with UCAN token creation for data import
|
|
// Convert import operation to UCAN token generation with import permissions
|
|
attenuations := []map[string]any{
|
|
{
|
|
"can": []string{"import", "write"},
|
|
"with": fmt.Sprintf("ipfs://%s", req.CID),
|
|
},
|
|
}
|
|
task, err := tasks.NewUCANTokenTask(
|
|
0,
|
|
"did:sonr:import-recipient",
|
|
attenuations,
|
|
time.Now().Add(1*time.Hour).Unix(),
|
|
)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
})
|
|
}
|
|
}
|
|
|
|
// RefreshHandler handles vault refresh requests
|
|
func (vh *VaultHandlers) RefreshHandler(client *asynq.Client) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
var req struct {
|
|
Enclave *mpc.EnclaveData `json:"enclave,omitempty"` // Direct enclave data
|
|
EnclaveCID string `json:"enclave_cid,omitempty"` // IPFS CID reference
|
|
Password []byte `json:"password,omitempty"` // Decryption password for IPFS stored data
|
|
Priority string `json:"priority,omitempty"`
|
|
}
|
|
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid JSON payload"})
|
|
}
|
|
|
|
// Handle enclave data - either direct or via IPFS CID
|
|
var enclave *mpc.EnclaveData
|
|
if req.EnclaveCID != "" {
|
|
// Retrieve enclave from IPFS
|
|
if vh.IPFSClient == nil {
|
|
return c.JSON(
|
|
http.StatusServiceUnavailable,
|
|
map[string]string{"error": "IPFS client not available"},
|
|
)
|
|
}
|
|
|
|
encryptedData, err := vh.IPFSClient.Get(req.EnclaveCID)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusNotFound,
|
|
map[string]string{"error": "Failed to retrieve enclave from IPFS"},
|
|
)
|
|
}
|
|
|
|
// Create temporary enclave for decryption
|
|
tempEnclave := &mpc.EnclaveData{}
|
|
decryptedData, err := tempEnclave.Decrypt(req.Password, encryptedData)
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to decrypt enclave data"},
|
|
)
|
|
}
|
|
|
|
// Unmarshal decrypted data
|
|
enclave = &mpc.EnclaveData{}
|
|
if err := enclave.Unmarshal(decryptedData); err != nil {
|
|
return c.JSON(
|
|
http.StatusBadRequest,
|
|
map[string]string{"error": "Failed to parse enclave data"},
|
|
)
|
|
}
|
|
} else if req.Enclave != nil {
|
|
// Use directly provided enclave data
|
|
enclave = req.Enclave
|
|
} else {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Either enclave or enclave_cid is required"})
|
|
}
|
|
|
|
// Refresh functionality replaced with UCAN DID generation for new identity
|
|
// Convert refresh operation to DID generation which includes key refresh
|
|
task, err := tasks.NewUCANDIDTask(0) // TODO: Extract userID from JWT or context
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to create task"},
|
|
)
|
|
}
|
|
|
|
queue := GetQueueFromPriority(req.Priority)
|
|
info, err := client.Enqueue(task, asynq.Queue(queue))
|
|
if err != nil {
|
|
return c.JSON(
|
|
http.StatusInternalServerError,
|
|
map[string]string{"error": "Failed to enqueue task"},
|
|
)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, map[string]any{
|
|
"task_id": info.ID,
|
|
"queue": queue,
|
|
"status": "enqueued",
|
|
})
|
|
}
|
|
}
|