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