handle-logs.go raw

   1  package app
   2  
   3  import (
   4  	"encoding/json"
   5  	"net/http"
   6  	"strconv"
   7  
   8  	lol "next.orly.dev/pkg/lol"
   9  	"next.orly.dev/pkg/lol/chk"
  10  
  11  	"next.orly.dev/pkg/nostr/httpauth"
  12  	"next.orly.dev/pkg/acl"
  13  	"next.orly.dev/pkg/logbuffer"
  14  )
  15  
  16  // LogsResponse is the response structure for GET /api/logs
  17  type LogsResponse struct {
  18  	Logs    []logbuffer.LogEntry `json:"logs"`
  19  	Total   int                  `json:"total"`
  20  	HasMore bool                 `json:"has_more"`
  21  }
  22  
  23  // LogLevelResponse is the response structure for GET /api/logs/level
  24  type LogLevelResponse struct {
  25  	Level string `json:"level"`
  26  }
  27  
  28  // LogLevelRequest is the request structure for POST /api/logs/level
  29  type LogLevelRequest struct {
  30  	Level string `json:"level"`
  31  }
  32  
  33  // handleGetLogs handles GET /api/logs
  34  func (s *Server) handleGetLogs(w http.ResponseWriter, r *http.Request) {
  35  	if r.Method != http.MethodGet {
  36  		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  37  		return
  38  	}
  39  
  40  	// Validate NIP-98 authentication
  41  	valid, pubkey, err := httpauth.CheckAuth(r)
  42  	if chk.E(err) || !valid {
  43  		errorMsg := "NIP-98 authentication validation failed"
  44  		if err != nil {
  45  			errorMsg = err.Error()
  46  		}
  47  		http.Error(w, errorMsg, http.StatusUnauthorized)
  48  		return
  49  	}
  50  
  51  	// Check permissions - require owner level only
  52  	accessLevel := acl.Registry.GetAccessLevel(pubkey, r.RemoteAddr)
  53  	if accessLevel != "owner" {
  54  		http.Error(w, "Owner permission required", http.StatusForbidden)
  55  		return
  56  	}
  57  
  58  	// Check if log buffer is available
  59  	if logbuffer.GlobalBuffer == nil {
  60  		http.Error(w, "Log buffer not enabled", http.StatusServiceUnavailable)
  61  		return
  62  	}
  63  
  64  	// Parse query parameters
  65  	offset := 0
  66  	limit := 100
  67  	if offsetStr := r.URL.Query().Get("offset"); offsetStr != "" {
  68  		if v, err := strconv.Atoi(offsetStr); err == nil && v >= 0 {
  69  			offset = v
  70  		}
  71  	}
  72  	if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
  73  		if v, err := strconv.Atoi(limitStr); err == nil && v > 0 && v <= 500 {
  74  			limit = v
  75  		}
  76  	}
  77  
  78  	// Get logs from buffer
  79  	logs := logbuffer.GlobalBuffer.Get(offset, limit)
  80  	total := logbuffer.GlobalBuffer.Count()
  81  	hasMore := offset+len(logs) < total
  82  
  83  	response := LogsResponse{
  84  		Logs:    logs,
  85  		Total:   total,
  86  		HasMore: hasMore,
  87  	}
  88  
  89  	w.Header().Set("Content-Type", "application/json")
  90  	json.NewEncoder(w).Encode(response)
  91  }
  92  
  93  // handleClearLogs handles POST /api/logs/clear
  94  func (s *Server) handleClearLogs(w http.ResponseWriter, r *http.Request) {
  95  	if r.Method != http.MethodPost {
  96  		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  97  		return
  98  	}
  99  
 100  	// Validate NIP-98 authentication
 101  	valid, pubkey, err := httpauth.CheckAuth(r)
 102  	if chk.E(err) || !valid {
 103  		errorMsg := "NIP-98 authentication validation failed"
 104  		if err != nil {
 105  			errorMsg = err.Error()
 106  		}
 107  		http.Error(w, errorMsg, http.StatusUnauthorized)
 108  		return
 109  	}
 110  
 111  	// Check permissions - require owner level only
 112  	accessLevel := acl.Registry.GetAccessLevel(pubkey, r.RemoteAddr)
 113  	if accessLevel != "owner" {
 114  		http.Error(w, "Owner permission required", http.StatusForbidden)
 115  		return
 116  	}
 117  
 118  	// Check if log buffer is available
 119  	if logbuffer.GlobalBuffer == nil {
 120  		http.Error(w, "Log buffer not enabled", http.StatusServiceUnavailable)
 121  		return
 122  	}
 123  
 124  	// Clear the buffer
 125  	logbuffer.GlobalBuffer.Clear()
 126  
 127  	w.Header().Set("Content-Type", "application/json")
 128  	json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
 129  }
 130  
 131  // handleLogLevel handles GET and POST /api/logs/level
 132  func (s *Server) handleLogLevel(w http.ResponseWriter, r *http.Request) {
 133  	switch r.Method {
 134  	case http.MethodGet:
 135  		s.handleGetLogLevel(w, r)
 136  	case http.MethodPost:
 137  		s.handleSetLogLevel(w, r)
 138  	default:
 139  		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 140  	}
 141  }
 142  
 143  // handleGetLogLevel handles GET /api/logs/level
 144  func (s *Server) handleGetLogLevel(w http.ResponseWriter, r *http.Request) {
 145  	// No auth required for reading log level
 146  	level := logbuffer.GetCurrentLevel()
 147  
 148  	w.Header().Set("Content-Type", "application/json")
 149  	json.NewEncoder(w).Encode(LogLevelResponse{Level: level})
 150  }
 151  
 152  // handleSetLogLevel handles POST /api/logs/level
 153  func (s *Server) handleSetLogLevel(w http.ResponseWriter, r *http.Request) {
 154  	// Validate NIP-98 authentication
 155  	valid, pubkey, err := httpauth.CheckAuth(r)
 156  	if chk.E(err) || !valid {
 157  		errorMsg := "NIP-98 authentication validation failed"
 158  		if err != nil {
 159  			errorMsg = err.Error()
 160  		}
 161  		http.Error(w, errorMsg, http.StatusUnauthorized)
 162  		return
 163  	}
 164  
 165  	// Check permissions - require owner level only
 166  	accessLevel := acl.Registry.GetAccessLevel(pubkey, r.RemoteAddr)
 167  	if accessLevel != "owner" {
 168  		http.Error(w, "Owner permission required", http.StatusForbidden)
 169  		return
 170  	}
 171  
 172  	// Parse request body
 173  	var req LogLevelRequest
 174  	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 175  		http.Error(w, "Invalid request body", http.StatusBadRequest)
 176  		return
 177  	}
 178  
 179  	// Validate and set log level
 180  	level := logbuffer.SetCurrentLevel(req.Level)
 181  	lol.SetLogLevel(level)
 182  
 183  	w.Header().Set("Content-Type", "application/json")
 184  	json.NewEncoder(w).Encode(LogLevelResponse{Level: level})
 185  }
 186