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)
|
logger.Error("failed to start server", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
go srv.Serve()
|
||||||
log.Info("server started", "address", srv.Address())
|
log.Info("server started", "address", srv.Address())
|
||||||
defer func() {
|
defer func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -17,6 +22,8 @@ import (
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
|
listener net.Listener
|
||||||
|
mux *http.ServeMux
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
connections map[*websocket.Conn]struct{}
|
connections map[*websocket.Conn]struct{}
|
||||||
address string
|
address string
|
||||||
|
@ -58,27 +65,28 @@ func (s *Server) Listen(address string) error {
|
||||||
ReadHeaderTimeout: 5 * time.Second,
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
s.mux = http.NewServeMux()
|
||||||
mux.HandleFunc("/signals", s.signals)
|
s.mux.HandleFunc("/signals", s.signals)
|
||||||
s.server.Handler = mux
|
s.server.Handler = s.mux
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", address)
|
var err error
|
||||||
|
s.listener, err = net.Listen("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.address = listener.Addr().String()
|
s.address = s.listener.Addr().String()
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := s.server.Serve(listener)
|
|
||||||
if !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
log.Error("signals server closed with error: %w", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) Stop(ctx context.Context) {
|
func (s *Server) Stop(ctx context.Context) {
|
||||||
for connection := range s.connections {
|
for connection := range s.connections {
|
||||||
err := connection.Close()
|
err := connection.Close()
|
||||||
|
@ -115,3 +123,68 @@ func (s *Server) signals(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
s.connections[connection] = struct{}{}
|
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)
|
return string(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use InitializeApplication instead.
|
||||||
func OpenAccounts(datadir string) string {
|
func OpenAccounts(datadir string) string {
|
||||||
return logAndCallString(openAccounts, datadir)
|
return logAndCallString(openAccounts, datadir)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue