mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
@@ -0,0 +1,535 @@
|
||||
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",
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user