feat(cmd)_: status-backend
This commit is contained in:
parent
946ee4e496
commit
6696e6fb9e
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"github.com/status-im/status-go/cmd/statusd/server"
|
||||
)
|
||||
|
||||
var (
|
||||
address = flag.String("address", "", "host:port to listen")
|
||||
logger = log.New("package", "status-go/cmd/status-backend")
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
srv := server.NewServer()
|
||||
srv.Setup()
|
||||
|
||||
err := srv.Listen(*address)
|
||||
if err != nil {
|
||||
logger.Error("failed to start server", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("server started", "address", srv.Address())
|
||||
srv.RegisterMobileAPI()
|
||||
srv.Serve()
|
||||
}
|
|
@ -178,6 +178,7 @@ func main() {
|
|||
logger.Error("failed to start server", "error", err)
|
||||
return
|
||||
}
|
||||
go srv.Serve()
|
||||
log.Info("server started", "address", srv.Address())
|
||||
defer func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package server
|
||||
|
||||
import "github.com/status-im/status-go/mobile"
|
||||
|
||||
var EndpointsWithResponse = []func(string) string {
|
||||
statusgo.InitializeApplication,
|
||||
statusgo.ExtractGroupMembershipSignatures,
|
||||
statusgo.SignGroupMembership,
|
||||
statusgo.ValidateNodeConfig,
|
||||
statusgo.CallRPC,
|
||||
statusgo.CallPrivateRPC,
|
||||
statusgo.CreateAccountAndLogin,
|
||||
statusgo.LoginAccount,
|
||||
statusgo.RestoreAccountAndLogin,
|
||||
statusgo.InitKeystore,
|
||||
statusgo.SignMessage,
|
||||
statusgo.HashTypedData,
|
||||
statusgo.HashTypedDataV4,
|
||||
statusgo.Recover,
|
||||
statusgo.HashTransaction,
|
||||
statusgo.HashMessage,
|
||||
statusgo.StartCPUProfile,
|
||||
statusgo.WriteHeapProfile,
|
||||
statusgo.AddPeer,
|
||||
statusgo.SignHash,
|
||||
statusgo.GenerateAlias,
|
||||
statusgo.IsAlias,
|
||||
statusgo.Identicon,
|
||||
statusgo.EmojiHash,
|
||||
statusgo.ColorHash,
|
||||
statusgo.ColorID,
|
||||
statusgo.ValidateMnemonic,
|
||||
statusgo.DecompressPublicKey,
|
||||
statusgo.CompressPublicKey,
|
||||
statusgo.SerializeLegacyKey,
|
||||
statusgo.GetPasswordStrength,
|
||||
statusgo.GetPasswordStrengthScore,
|
||||
statusgo.GetConnectionStringForBeingBootstrapped,
|
||||
statusgo.GetConnectionStringForBootstrappingAnotherDevice,
|
||||
statusgo.GetConnectionStringForExportingKeypairsKeystores,
|
||||
statusgo.ValidateConnectionString,
|
||||
statusgo.DecodeParameters,
|
||||
statusgo.HexToNumber,
|
||||
statusgo.NumberToHex,
|
||||
statusgo.Sha3,
|
||||
statusgo.Utf8ToHex,
|
||||
statusgo.HexToUtf8,
|
||||
statusgo.CheckAddressChecksum,
|
||||
statusgo.IsAddress,
|
||||
statusgo.ToChecksumAddress,
|
||||
statusgo.DeserializeAndCompressKey,
|
||||
statusgo.InitLogging,
|
||||
statusgo.ToggleCentralizedMetrics,
|
||||
statusgo.AddCentralizedMetric,
|
||||
}
|
||||
|
||||
var EndpointsNoRequest = []func() string {
|
||||
statusgo.GetNodeConfig,
|
||||
statusgo.ResetChainData,
|
||||
statusgo.Logout,
|
||||
statusgo.StopCPUProfiling,
|
||||
statusgo.StartLocalNotifications,
|
||||
statusgo.StopLocalNotifications,
|
||||
statusgo.ExportNodeLogs,
|
||||
statusgo.ImageServerTLSCert,
|
||||
statusgo.Fleets,
|
||||
statusgo.LocalPairingPreflightOutboundCheck,
|
||||
statusgo.StartSearchForLocalPairingPeers,
|
||||
statusgo.GetRandomMnemonic,
|
||||
statusgo.CentralizedMetricsInfo,
|
||||
}
|
||||
|
||||
var EndpointsUnsupported = []string {
|
||||
"VerifyAccountPassword",
|
||||
"VerifyDatabasePassword",
|
||||
"MigrateKeyStoreDir",
|
||||
"DeleteMultiaccount",
|
||||
"DeleteImportedKey",
|
||||
"SignTypedData",
|
||||
"SignTypedDataV4",
|
||||
"SendTransactionWithChainID",
|
||||
"SendTransaction",
|
||||
"SendTransactionWithSignature",
|
||||
"ConnectionChange",
|
||||
"AppStateChange",
|
||||
"SetMobileSignalHandler",
|
||||
"SetSignalEventCallback",
|
||||
"MultiformatSerializePublicKey",
|
||||
"MultiformatDeserializePublicKey",
|
||||
"ExportUnencryptedDatabase",
|
||||
"ImportUnencryptedDatabase",
|
||||
"ChangeDatabasePassword",
|
||||
"ConvertToKeycardAccount",
|
||||
"ConvertToRegularAccount",
|
||||
"SwitchFleet",
|
||||
"InputConnectionStringForBootstrapping",
|
||||
"InputConnectionStringForBootstrappingAnotherDevice",
|
||||
"InputConnectionStringForImportingKeypairsKeystores",
|
||||
"EncodeTransfer",
|
||||
"EncodeFunctionCall",
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package server
|
||||
|
||||
import "github.com/status-im/status-go/mobile"
|
||||
|
||||
var EndpointsWithResponse = []func(string) string {
|
||||
{{- range .FunctionsWithResp }}
|
||||
{{ $.PackageName }}.{{ . }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
var EndpointsNoRequest = []func() string {
|
||||
{{- range .FunctionsNoArgs }}
|
||||
{{ $.PackageName }}.{{ . }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
var EndpointsUnsupported = []string {
|
||||
{{- range .UnsupportedEndpoints }}
|
||||
"{{ . }}",
|
||||
{{- end }}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
//go:generate go run main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
inputFilePath = "../../../../mobile/status.go"
|
||||
templateFilePath = "./endpoints_template.txt"
|
||||
outputFilePath = "../endpoints.go"
|
||||
)
|
||||
|
||||
var (
|
||||
// Regular expressions extracted as global variables
|
||||
publicFunc = regexp.MustCompile(`func\s+([A-Z]\w+)\(.*\).*\{`)
|
||||
publicFuncWithRespPattern = regexp.MustCompile(`^func\s+([A-Z]\w*)\((\w|\s)+\)\s+string\s+\{$`)
|
||||
publicFuncNoArgsPattern = regexp.MustCompile(`^func\s+([A-Z]\w*)\(\)\s+string\s+\{$`)
|
||||
funcNamePattern = regexp.MustCompile(`^func\s+([A-Z]\w*)\(`)
|
||||
)
|
||||
|
||||
type TemplateData struct {
|
||||
PackageName string
|
||||
FunctionsWithResp []string
|
||||
FunctionsNoArgs []string
|
||||
UnsupportedEndpoints []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Open the Go source file
|
||||
file, err := os.Open(inputFilePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open file: %s\n", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
var publicFunctionsWithResp []string
|
||||
var publicFunctionsNoArgs []string
|
||||
var unsupportedFunctions []string
|
||||
var isDeprecated bool
|
||||
var packageName string
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
// Detect package name
|
||||
if strings.HasPrefix(line, "package ") {
|
||||
packageName = strings.TrimPrefix(line, "package ")
|
||||
}
|
||||
|
||||
// Check for deprecation comment
|
||||
if isDeprecatedComment(line) {
|
||||
isDeprecated = true
|
||||
continue
|
||||
}
|
||||
|
||||
if !publicFunc.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
|
||||
if isDeprecated {
|
||||
isDeprecated = false
|
||||
continue
|
||||
}
|
||||
|
||||
functionName := extractFunctionName(line)
|
||||
|
||||
switch {
|
||||
case isPublicFunctionWithResp(line):
|
||||
publicFunctionsWithResp = append(publicFunctionsWithResp, functionName)
|
||||
continue
|
||||
case isPublicFunctionNoArgs(line):
|
||||
publicFunctionsNoArgs = append(publicFunctionsNoArgs, functionName)
|
||||
continue
|
||||
default:
|
||||
unsupportedFunctions = append(unsupportedFunctions, functionName)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Printf("Error reading file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare the template data
|
||||
data := TemplateData{
|
||||
PackageName: packageName,
|
||||
FunctionsWithResp: publicFunctionsWithResp,
|
||||
FunctionsNoArgs: publicFunctionsNoArgs,
|
||||
UnsupportedEndpoints: unsupportedFunctions,
|
||||
}
|
||||
|
||||
// Load and parse the template
|
||||
tmpl, err := template.ParseFiles(templateFilePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to parse template file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create the output file
|
||||
outputFile, err := os.Create(outputFilePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create output file: %s\n", err)
|
||||
return
|
||||
}
|
||||
defer outputFile.Close()
|
||||
|
||||
// Execute the template and write the result to the output file
|
||||
err = tmpl.Execute(outputFile, data)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to execute template: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Generated endpoints file:", outputFilePath)
|
||||
}
|
||||
|
||||
// Function to check if a line contains a public function with a response of string
|
||||
func isPublicFunctionWithResp(line string) bool {
|
||||
return publicFuncWithRespPattern.MatchString(line)
|
||||
}
|
||||
|
||||
// Function to check if a line contains a public function with not arguments and a response of string
|
||||
func isPublicFunctionNoArgs(line string) bool {
|
||||
return publicFuncNoArgsPattern.MatchString(line)
|
||||
}
|
||||
|
||||
// Function to extract the public function name from a line
|
||||
func extractFunctionName(line string) string {
|
||||
matches := funcNamePattern.FindStringSubmatch(line)
|
||||
if len(matches) > 1 {
|
||||
return matches[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Function to check if a comment indicates a deprecated function
|
||||
func isDeprecatedComment(line string) bool {
|
||||
return strings.Contains(line, "// Deprecated:")
|
||||
}
|
|
@ -3,8 +3,13 @@ package server
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -17,6 +22,8 @@ import (
|
|||
|
||||
type Server struct {
|
||||
server *http.Server
|
||||
listener net.Listener
|
||||
mux *http.ServeMux
|
||||
lock sync.Mutex
|
||||
connections map[*websocket.Conn]struct{}
|
||||
address string
|
||||
|
@ -58,25 +65,26 @@ func (s *Server) Listen(address string) error {
|
|||
ReadHeaderTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/signals", s.signals)
|
||||
s.server.Handler = mux
|
||||
s.mux = http.NewServeMux()
|
||||
s.mux.HandleFunc("/signals", s.signals)
|
||||
s.server.Handler = s.mux
|
||||
|
||||
listener, err := net.Listen("tcp", address)
|
||||
var err error
|
||||
s.listener, err = net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.address = listener.Addr().String()
|
||||
s.address = s.listener.Addr().String()
|
||||
|
||||
go func() {
|
||||
err := s.server.Serve(listener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Serve() {
|
||||
err := s.server.Serve(s.listener)
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error("signals server closed with error: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Stop(ctx context.Context) {
|
||||
|
@ -115,3 +123,68 @@ func (s *Server) signals(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
s.connections[connection] = struct{}{}
|
||||
}
|
||||
|
||||
func (s *Server) addEndpointWithResponse(handler func(string) string) {
|
||||
endpoint := endpointName(functionName(handler))
|
||||
log.Info("adding endpoint", "name", endpoint)
|
||||
s.mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
request, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Error("failed to read request: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
response := handler(string(request))
|
||||
|
||||
_, err = w.Write([]byte(response))
|
||||
if err != nil {
|
||||
log.Error("failed to write response: %w", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) addEndpointNoRequest(handler func() string) {
|
||||
endpoint := endpointName(functionName(handler))
|
||||
log.Info("adding endpoint", "name", endpoint)
|
||||
s.mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
response := handler()
|
||||
|
||||
_, err := w.Write([]byte(response))
|
||||
if err != nil {
|
||||
log.Error("failed to write response: %w", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) addUnsupportedEndpoint(name string) {
|
||||
endpoint := endpointName(name)
|
||||
log.Info("marking unsupported endpoint", "name", endpoint)
|
||||
s.mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) RegisterMobileAPI() {
|
||||
for _, endpoint := range EndpointsWithResponse {
|
||||
s.addEndpointWithResponse(endpoint)
|
||||
}
|
||||
for _, endpoint := range EndpointsNoRequest {
|
||||
s.addEndpointNoRequest(endpoint)
|
||||
}
|
||||
for _, endpoint := range EndpointsUnsupported {
|
||||
s.addUnsupportedEndpoint(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
func functionName(fn any) string {
|
||||
fullName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
||||
parts := strings.Split(fullName, "/")
|
||||
lastPart := parts[len(parts)-1]
|
||||
nameParts := strings.Split(lastPart, ".")
|
||||
return nameParts[len(nameParts)-1]
|
||||
}
|
||||
|
||||
func endpointName(functionName string) string {
|
||||
const base = "statusgo"
|
||||
return fmt.Sprintf("/%s/%s", base, functionName)
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ func initializeApplication(requestJSON string) string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// Deprecated: Use InitializeApplication instead.
|
||||
func OpenAccounts(datadir string) string {
|
||||
return logAndCallString(openAccounts, datadir)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue