Files
meta-core/internal/api/kv.go
T

187 lines
4.1 KiB
Go

package api
import (
"net/http"
"net/url"
"strconv"
"strings"
"github.com/gorilla/mux"
)
// KVInfoResponse is the response for GET /api/kv/info
type KVInfoResponse struct {
Prefix string `json:"prefix"`
FileCount int `json:"fileCount"`
KeyCount int `json:"keyCount"`
TotalSize int64 `json:"totalSize"`
MemoryUsage string `json:"memoryUsage"`
}
// KVKeysResponse is the response for GET /api/kv/keys
type KVKeysResponse struct {
Keys []string `json:"keys"`
Cursor string `json:"cursor"`
HasMore bool `json:"hasMore"`
}
// KVKeyValueResponse is the response for GET /api/kv/key/{key}
type KVKeyValueResponse struct {
Key string `json:"key"`
Type string `json:"type"`
Value map[string]string `json:"value"`
}
// handleKVInfo handles GET /api/kv/info
func (s *Server) handleKVInfo(w http.ResponseWriter, r *http.Request) {
if !s.storage.IsConnected() {
writeError(w, http.StatusServiceUnavailable, "storage not connected")
return
}
// Get all hash IDs to count files
hashIDs, err := s.storage.GetAllHashIDs()
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
// Get memory usage
memoryUsage, err := s.storage.GetMemoryInfo()
if err != nil {
memoryUsage = "N/A"
}
// Calculate total size from sizeByte metadata (if available)
var totalSize int64
for _, hashID := range hashIDs {
sizeStr, err := s.storage.GetProperty(hashID, "sizeByte")
if err == nil && sizeStr != "" {
if size, err := strconv.ParseInt(sizeStr, 10, 64); err == nil {
totalSize += size
}
}
}
response := KVInfoResponse{
Prefix: "file:",
FileCount: len(hashIDs),
KeyCount: len(hashIDs),
TotalSize: totalSize,
MemoryUsage: memoryUsage,
}
writeJSON(w, http.StatusOK, response)
}
// handleKVKeys handles GET /api/kv/keys
func (s *Server) handleKVKeys(w http.ResponseWriter, r *http.Request) {
if !s.storage.IsConnected() {
writeError(w, http.StatusServiceUnavailable, "storage not connected")
return
}
// Parse query parameters
cursorStr := r.URL.Query().Get("cursor")
countStr := r.URL.Query().Get("count")
cursor := 0
if cursorStr != "" {
if c, err := strconv.Atoi(cursorStr); err == nil {
cursor = c
}
}
count := 50
if countStr != "" {
if c, err := strconv.Atoi(countStr); err == nil && c > 0 {
count = c
}
}
// Get all hash IDs
allHashIDs, err := s.storage.GetAllHashIDs()
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
// Simple offset-based pagination
var keys []string
end := cursor + count
if end > len(allHashIDs) {
end = len(allHashIDs)
}
if cursor < len(allHashIDs) {
for _, id := range allHashIDs[cursor:end] {
keys = append(keys, "file:"+id)
}
}
nextCursor := cursor + len(keys)
hasMore := nextCursor < len(allHashIDs)
response := KVKeysResponse{
Keys: keys,
Cursor: strconv.Itoa(nextCursor),
HasMore: hasMore,
}
if !hasMore {
response.Cursor = "0"
}
writeJSON(w, http.StatusOK, response)
}
// handleKVGetKey handles GET /api/kv/key/{key}
func (s *Server) handleKVGetKey(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
key := vars["key"]
if key == "" {
writeError(w, http.StatusBadRequest, "key is required")
return
}
// URL decode the key (in case it contains encoded characters)
decodedKey, err := url.PathUnescape(key)
if err != nil {
decodedKey = key
}
if !s.storage.IsConnected() {
writeError(w, http.StatusServiceUnavailable, "storage not connected")
return
}
// Extract hash ID from key (format: file:{hashId})
if !strings.HasPrefix(decodedKey, "file:") {
writeError(w, http.StatusBadRequest, "invalid key format, expected: file:{hashId}")
return
}
hashID := strings.TrimPrefix(decodedKey, "file:")
// Get metadata for this hash ID
metadata, err := s.storage.GetMetadataFlat(hashID)
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
if metadata == nil {
writeError(w, http.StatusNotFound, "key not found")
return
}
response := KVKeyValueResponse{
Key: decodedKey,
Type: "hash",
Value: metadata,
}
writeJSON(w, http.StatusOK, response)
}