feat(daemon): add REST API endpoints for web frontends
Some checks are pending
Security Scan / security (push) Waiting to run
Test / Test (push) Waiting to run

/api/info    — chain status (no JSON-RPC wrapper)
/api/block   — block by height
/api/aliases — all aliases
/api/alias   — single alias by name
/api/search  — universal search
/health      — node health check

REST endpoints serve raw JSON — simpler for web apps than JSON-RPC.
40 RPC + 16 wallet + 11 HTTP = 67 total endpoints.

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Claude 2026-04-02 02:43:15 +01:00
parent d2655921d1
commit 7c38e6a207
No known key found for this signature in database
GPG key ID: AF404715446AEB41

View file

@ -41,6 +41,12 @@ func NewServer(c *chain.Chain, cfg *config.ChainConfig) *Server {
s.mux.HandleFunc("/json_rpc", s.handleJSONRPC)
s.mux.HandleFunc("/getheight", s.handleGetHeight)
s.mux.HandleFunc("/start_mining", s.handleStartMining)
s.mux.HandleFunc("/api/info", s.handleRESTInfo)
s.mux.HandleFunc("/api/block", s.handleRESTBlock)
s.mux.HandleFunc("/api/aliases", s.handleRESTAliases)
s.mux.HandleFunc("/api/alias", s.handleRESTAlias)
s.mux.HandleFunc("/api/search", s.handleRESTSearch)
s.mux.HandleFunc("/health", s.handleRESTHealth)
s.mux.HandleFunc("/gettransactions", s.handleGetTransactions)
s.mux.HandleFunc("/stop_mining", s.handleStopMining)
return s
@ -1366,3 +1372,87 @@ func (s *Server) rpcGetDifficultyHistory(w http.ResponseWriter, req jsonRPCReque
"status": "OK",
})
}
// --- REST-style HTTP endpoints (no JSON-RPC wrapper) ---
func (s *Server) handleRESTInfo(w http.ResponseWriter, r *http.Request) {
height, _ := s.chain.Height()
_, meta, _ := s.chain.TopBlock()
aliases := s.chain.GetAllAliases()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"height": height, "difficulty": meta.Difficulty,
"aliases": len(aliases), "node": "CoreChain/Go",
})
}
func (s *Server) handleRESTBlock(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("height")
h, err := parseUint64(q)
if err != nil {
w.WriteHeader(400)
json.NewEncoder(w).Encode(map[string]string{"error": "?height= required"})
return
}
blk, meta, err := s.chain.GetBlockByHeight(h)
if err != nil {
w.WriteHeader(404)
json.NewEncoder(w).Encode(map[string]string{"error": "not found"})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"height": meta.Height, "hash": meta.Hash.String(),
"timestamp": blk.Timestamp, "difficulty": meta.Difficulty,
"tx_count": len(blk.TxHashes), "version": blk.MajorVersion,
})
}
func (s *Server) handleRESTAliases(w http.ResponseWriter, r *http.Request) {
aliases := s.chain.GetAllAliases()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(aliases)
}
func (s *Server) handleRESTAlias(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
w.WriteHeader(400)
json.NewEncoder(w).Encode(map[string]string{"error": "?name= required"})
return
}
alias, err := s.chain.GetAlias(name)
if err != nil {
w.WriteHeader(404)
json.NewEncoder(w).Encode(map[string]string{"error": "not found"})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(alias)
}
func (s *Server) handleRESTSearch(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q")
if q == "" {
w.WriteHeader(400)
json.NewEncoder(w).Encode(map[string]string{"error": "?q= required"})
return
}
// Reuse the RPC search logic
fakeReq := jsonRPCRequest{Params: json.RawMessage(core.Sprintf(`{"query":"%s"}`, q))}
s.rpcSearch(w, fakeReq)
}
func (s *Server) handleRESTHealth(w http.ResponseWriter, r *http.Request) {
height, _ := s.chain.Height()
aliases := s.chain.GetAllAliases()
status := "ok"
if height == 0 {
status = "syncing"
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": status, "height": height,
"aliases": len(aliases), "node": "CoreChain/Go",
})
}