mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 16:13:04 +00:00
94 lines
2.8 KiB
Go
94 lines
2.8 KiB
Go
package storage
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/httputil"
|
|
"github.com/DeBrosOfficial/network/pkg/logging"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// PinHandler handles POST /v1/storage/pin.
|
|
// It pins an existing CID in the IPFS cluster, ensuring the content
|
|
// is replicated across the configured number of cluster peers.
|
|
func (h *Handlers) PinHandler(w http.ResponseWriter, r *http.Request) {
|
|
if h.ipfsClient == nil {
|
|
httputil.WriteError(w, http.StatusServiceUnavailable, "IPFS storage not available")
|
|
return
|
|
}
|
|
|
|
if !httputil.CheckMethod(w, r, http.MethodPost) {
|
|
return
|
|
}
|
|
|
|
var req StoragePinRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
httputil.WriteError(w, http.StatusBadRequest, fmt.Sprintf("failed to decode request: %v", err))
|
|
return
|
|
}
|
|
|
|
if req.Cid == "" {
|
|
httputil.WriteError(w, http.StatusBadRequest, "cid required")
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
|
|
// Get namespace from context for ownership check
|
|
namespace := h.getNamespaceFromContext(ctx)
|
|
if namespace == "" {
|
|
httputil.WriteError(w, http.StatusUnauthorized, "namespace required")
|
|
return
|
|
}
|
|
|
|
// Check if namespace owns this CID (namespace isolation)
|
|
hasAccess, err := h.checkCIDOwnership(ctx, req.Cid, namespace)
|
|
if err != nil {
|
|
h.logger.ComponentError(logging.ComponentGeneral, "failed to check CID ownership",
|
|
zap.Error(err), zap.String("cid", req.Cid), zap.String("namespace", namespace))
|
|
httputil.WriteError(w, http.StatusInternalServerError, "failed to verify access")
|
|
return
|
|
}
|
|
if !hasAccess {
|
|
h.logger.ComponentWarn(logging.ComponentGeneral, "namespace attempted to pin CID they don't own",
|
|
zap.String("cid", req.Cid), zap.String("namespace", namespace))
|
|
httputil.WriteError(w, http.StatusForbidden, "access denied: CID not owned by namespace")
|
|
return
|
|
}
|
|
|
|
// Get replication factor from config (default: 3)
|
|
replicationFactor := h.config.IPFSReplicationFactor
|
|
if replicationFactor == 0 {
|
|
replicationFactor = 3
|
|
}
|
|
|
|
pinResp, err := h.ipfsClient.Pin(ctx, req.Cid, req.Name, replicationFactor)
|
|
if err != nil {
|
|
h.logger.ComponentError(logging.ComponentGeneral, "failed to pin CID",
|
|
zap.Error(err), zap.String("cid", req.Cid))
|
|
httputil.WriteError(w, http.StatusInternalServerError, fmt.Sprintf("failed to pin: %v", err))
|
|
return
|
|
}
|
|
|
|
// Update pin status in database
|
|
if err := h.updatePinStatus(ctx, req.Cid, namespace, true); err != nil {
|
|
h.logger.ComponentWarn(logging.ComponentGeneral, "failed to update pin status in database (non-fatal)",
|
|
zap.Error(err), zap.String("cid", req.Cid))
|
|
}
|
|
|
|
// Use name from request if response doesn't have it
|
|
name := pinResp.Name
|
|
if name == "" {
|
|
name = req.Name
|
|
}
|
|
|
|
response := StoragePinResponse{
|
|
Cid: pinResp.Cid,
|
|
Name: name,
|
|
}
|
|
|
|
httputil.WriteJSON(w, http.StatusOK, response)
|
|
}
|