diff --git a/cli/main.go b/cli/main.go index 80fc9397..f23afe23 100644 --- a/cli/main.go +++ b/cli/main.go @@ -6,6 +6,24 @@ import ( "github.com/hiddify/hiddify-core/cmd" ) +type UpdateRequest struct { + Description string `json:"description,omitempty"` + PrivatePods bool `json:"private_pods"` + OperatingMode string `json:"operating_mode,omitempty"` + ActivationState string `json:"activation_state,omitempty"` +} + func main() { cmd.ParseCli(os.Args[1:]) + + // var request UpdateRequest + // // jsonTag, err2 := validation.ErrorFieldName(&request, &request.OperatingMode) + // jsonTag, err2 := request.ValName(&request.OperatingMode) + + // fmt.Println(jsonTag, err2) + // RegisterExtension("com.example.extension", NewExampleExtension()) + // ex := extensionsMap["com.example.extension"].(*Extension[struct]) + // fmt.Println(NewExampleExtension().Get()) + + // fmt.Println(ex.Get()) } diff --git a/cmd.sh b/cmd.sh old mode 100644 new mode 100755 index bb4b219a..90a39f35 --- a/cmd.sh +++ b/cmd.sh @@ -1,3 +1,3 @@ TAGS=with_gvisor,with_quic,with_wireguard,with_ech,with_utls,with_clash_api,with_grpc # TAGS=with_dhcp,with_low_memory,with_conntrack -go run --tags $TAGS ./cmd $@ \ No newline at end of file +go run --tags $TAGS ./cli $@ \ No newline at end of file diff --git a/cmd/cmd_instance.go b/cmd/cmd_instance.go new file mode 100644 index 00000000..936ed252 --- /dev/null +++ b/cmd/cmd_instance.go @@ -0,0 +1,59 @@ +package cmd + +import ( + "os" + "os/signal" + "syscall" + + v2 "github.com/hiddify/hiddify-core/v2" + "github.com/sagernet/sing-box/log" + "github.com/spf13/cobra" +) + +var commandInstance = &cobra.Command{ + Use: "instance", + Short: "instance", + Args: cobra.OnlyValidArgs, + Run: func(cmd *cobra.Command, args []string) { + hiddifySetting := defaultConfigs + if hiddifySettingPath != "" { + hiddifySetting2, err := v2.ReadHiddifyOptionsAt(hiddifySettingPath) + if err != nil { + log.Fatal(err) + } + hiddifySetting = *hiddifySetting2 + } + + instance, err := v2.RunInstanceString(&hiddifySetting, configPath) + if err != nil { + log.Fatal(err) + } + defer instance.Close() + ping, err := instance.PingAverage("http://cp.cloudflare.com", 4) + if err != nil { + // log.Fatal(err) + } + log.Info("Average Ping to Cloudflare : ", ping, "\n") + + for i := 1; i <= 4; i++ { + ping, err := instance.PingCloudflare() + if err != nil { + log.Warn(i, " Error ", err, "\n") + } else { + log.Info(i, " Ping time: ", ping, " ms\n") + } + } + log.Info("Instance is running on port socks5://127.0.0.1:", instance.ListenPort, "\n") + log.Info("Press Ctrl+C to exit\n") + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + <-sigChan + log.Info("CTRL+C recived-->stopping\n") + instance.Close() + }, +} + +func init() { + mainCommand.AddCommand(commandInstance) + addHConfigFlags(commandInstance) +} diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index e71f8c5b..9f19ed5f 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -23,6 +23,5 @@ func init() { } func runCommand(cmd *cobra.Command, args []string) { - v2.RunStandalone(hiddifySettingPath, configPath, defaultConfigs) } diff --git a/cmd/cmd_temp.go b/cmd/cmd_temp.go new file mode 100644 index 00000000..a1cd7b9b --- /dev/null +++ b/cmd/cmd_temp.go @@ -0,0 +1,141 @@ +package cmd + +import ( + "context" + "fmt" + "io" + "math/rand" + "net/http" + "net/netip" + "time" + + "github.com/hiddify/hiddify-core/common" + "github.com/hiddify/hiddify-core/extension_repository/cleanip_scanner" + "github.com/spf13/cobra" + "golang.org/x/net/proxy" +) + +var commandTemp = &cobra.Command{ + Use: "temp", + Short: "temp", + Args: cobra.MaximumNArgs(2), + Run: func(cmd *cobra.Command, args []string) { + // fmt.Printf("Ping time: %d ms\n", Ping()) + scanner := cleanip_scanner.NewScannerEngine(&cleanip_scanner.ScannerOptions{ + UseIPv4: true, + UseIPv6: common.CanConnectIPv6(), + MaxDesirableRTT: 500 * time.Millisecond, + IPQueueSize: 4, + IPQueueTTL: 10 * time.Second, + ConcurrentPings: 10, + // MaxDesirableIPs: e.count, + CidrList: cleanip_scanner.DefaultCFRanges(), + PingFunc: func(ip netip.Addr) (cleanip_scanner.IPInfo, error) { + fmt.Printf("Ping: %s\n", ip.String()) + return cleanip_scanner.IPInfo{ + AddrPort: netip.AddrPortFrom(ip, 80), + RTT: time.Duration(rand.Intn(1000)), + CreatedAt: time.Now(), + }, nil + }, + }, + ) + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + scanner.Run(ctx) + + t := time.NewTicker(1 * time.Second) + defer t.Stop() + + for { + ipList := scanner.GetAvailableIPs(false) + if len(ipList) > 1 { + // e.result = "" + for i := 0; i < 2; i++ { + // result = append(result, ipList[i]) + // e.result = e.result + ipList[i].AddrPort.String() + "\n" + fmt.Printf("%d %s\n", ipList[i].RTT, ipList[i].AddrPort.String()) + } + return + } + + select { + case <-ctx.Done(): + // Context is done + return + case <-t.C: + // Prevent the loop from spinning too fast + continue + } + } + }, +} + +func init() { + mainCommand.AddCommand(commandTemp) +} + +func GetContent(url string) (string, error) { + return ContentFromURL("GET", url, 10*time.Second) +} + +func ContentFromURL(method string, url string, timeout time.Duration) (string, error) { + if method == "" { + return "", fmt.Errorf("empty method") + } + if url == "" { + return "", fmt.Errorf("empty url") + } + + req, err := http.NewRequest(method, url, nil) + if err != nil { + return "", err + } + + dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:12334", nil, proxy.Direct) + if err != nil { + return "", err + } + + transport := &http.Transport{ + Dial: dialer.Dial, + } + + client := &http.Client{ + Transport: transport, + Timeout: timeout, + } + + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { + return "", fmt.Errorf("request failed with status code: %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + if body == nil { + return "", fmt.Errorf("empty body") + } + + return string(body), nil +} + +func Ping() int { + startTime := time.Now() + _, err := ContentFromURL("HEAD", "https://cp.cloudflare.com", 4*time.Second) + if err != nil { + return -1 + } + duration := time.Since(startTime) + return int(duration.Milliseconds()) +} diff --git a/common/cache.go b/common/cache.go new file mode 100644 index 00000000..ceb225d5 --- /dev/null +++ b/common/cache.go @@ -0,0 +1,174 @@ +package common + +import ( + "context" + "encoding/json" + "errors" + "log" + "os" + "time" + + "github.com/sagernet/sing-box/option" + + "github.com/sagernet/bbolt" + bboltErrors "github.com/sagernet/bbolt/errors" + + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/service/filemanager" +) + +var ( + Storage = New(context.Background(), option.CacheFileOptions{}) + bucketExtension = []byte("extension") + bucketHiddify = []byte("hiddify") + + bucketNameList = []string{ + string(bucketExtension), + string(bucketHiddify), + } +) + +type CacheFile struct { + ctx context.Context + path string + cacheID []byte + + DB *bbolt.DB +} + +func New(ctx context.Context, options option.CacheFileOptions) *CacheFile { + var path string + if options.Path != "" { + path = options.Path + } else { + path = "hiddify.db" + } + var cacheIDBytes []byte + if options.CacheID != "" { + cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...) + } + cache := &CacheFile{ + ctx: ctx, + path: filemanager.BasePath(ctx, path), + cacheID: cacheIDBytes, + } + err := cache.start() + if err != nil { + log.Panic(err) + } + return cache +} + +func (c *CacheFile) start() error { + const fileMode = 0o666 + options := bbolt.Options{Timeout: time.Second + } + var ( + db *bbolt.DB + err error + ) + for i := 0; i < 10; i++ { + db, err = bbolt.Open(c.path, fileMode, &options) + if err == nil { + break + } + if errors.Is(err, bboltErrors.ErrTimeout) { + continue + } + if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) { + rmErr := os.Remove(c.path) + if rmErr != nil { + return err + } + } + time.Sleep(100 * time.Millisecond) + } + if err != nil { + return err + } + err = filemanager.Chown(c.ctx, c.path) + if err != nil { + db.Close() + return E.Cause(err, "platform chown") + } + err = db.Batch(func(tx *bbolt.Tx) error { + return tx.ForEach(func(name []byte, b *bbolt.Bucket) error { + if name[0] == 0 { + return b.ForEachBucket(func(k []byte) error { + bucketName := string(k) + if !(common.Contains(bucketNameList, bucketName)) { + _ = b.DeleteBucket(name) + } + return nil + }) + } else { + bucketName := string(name) + if !(common.Contains(bucketNameList, bucketName)) { + _ = tx.DeleteBucket(name) + } + } + return nil + }) + }) + if err != nil { + db.Close() + return err + } + c.DB = db + return nil +} + +func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket { + if c.cacheID == nil { + return t.Bucket(key) + } + bucket := t.Bucket(c.cacheID) + if bucket == nil { + return nil + } + return bucket.Bucket(key) +} + +func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) { + if c.cacheID == nil { + return t.CreateBucketIfNotExists(key) + } + bucket, err := t.CreateBucketIfNotExists(c.cacheID) + if bucket == nil { + return nil, err + } + return bucket.CreateBucketIfNotExists(key) +} + +func (c *CacheFile) GetExtensionData(extension_id string, default_value any) error { + err := c.DB.View(func(t *bbolt.Tx) error { + bucket := c.bucket(t, bucketExtension) + if bucket == nil { + return os.ErrNotExist + } + setBinary := bucket.Get([]byte(extension_id)) + if len(setBinary) == 0 { + return os.ErrInvalid + } + return json.Unmarshal(setBinary, &default_value) + }) + return err +} + +func (c *CacheFile) SaveExtensionData(extension_id string, data any) error { + return c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := c.createBucket(t, bucketExtension) + if err != nil { + return err + } + + // Assuming T implements MarshalBinary + + setBinary, err := json.MarshalIndent(data, " ", "") + if err != nil { + return err + } + return bucket.Put([]byte(extension_id), setBinary) + }) +} diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 00000000..21d9484f --- /dev/null +++ b/common/utils.go @@ -0,0 +1,25 @@ +package common + +import ( + "net" + "net/netip" + "time" +) + +func CanConnectIPv6Addr(remoteAddr netip.AddrPort) bool { + dialer := net.Dialer{ + Timeout: 1 * time.Second, + } + + conn, err := dialer.Dial("tcp6", remoteAddr.String()) + if err != nil { + return false + } + defer conn.Close() + + return true +} + +func CanConnectIPv6() bool { + return CanConnectIPv6Addr(netip.MustParseAddrPort("[2001:4860:4860::8888]:80")) +} diff --git a/config/config.go b/config/config.go index 45a12266..b9c35e54 100644 --- a/config/config.go +++ b/config/config.go @@ -287,7 +287,7 @@ func setClashAPI(options *option.Options, opt *HiddifyOptions) { func setLog(options *option.Options, opt *HiddifyOptions) { options.Log = &option.LogOptions{ Level: opt.LogLevel, - Output: "box.log", + Output: opt.LogFile, Disabled: false, Timestamp: true, DisableColor: true, diff --git a/config/option.go b/config/hiddify_option.go similarity index 96% rename from config/option.go rename to config/hiddify_option.go index 54f421cb..36fd367b 100644 --- a/config/option.go +++ b/config/hiddify_option.go @@ -8,6 +8,7 @@ import ( type HiddifyOptions struct { EnableFullConfig bool `json:"enable-full-config"` LogLevel string `json:"log-level"` + LogFile string `json:"log-file"` EnableClashApi bool `json:"enable-clash-api"` ClashApiPort uint16 `json:"clash-api-port"` ClashApiSecret string `json:"web-secret"` @@ -106,8 +107,8 @@ func DefaultHiddifyOptions() *HiddifyOptions { InboundOptions: InboundOptions{ EnableTun: false, SetSystemProxy: false, - MixedPort: 2334, - TProxyPort: 2335, + MixedPort: 12334, + TProxyPort: 12335, LocalDnsPort: 16450, MTU: 9000, StrictRoute: true, @@ -124,10 +125,12 @@ func DefaultHiddifyOptions() *HiddifyOptions { BypassLAN: false, AllowConnectionFromLAN: false, }, - LogLevel: "warn", + LogLevel: "warn", + // LogFile: "/dev/null", + LogFile: "box.log", Region: "other", EnableClashApi: true, - ClashApiPort: 6756, + ClashApiPort: 16756, ClashApiSecret: "", // GeoIPPath: "geoip.db", // GeoSitePath: "geosite.db", diff --git a/config/parser.go b/config/parser.go index 10bd1e08..b6e23de8 100644 --- a/config/parser.go +++ b/config/parser.go @@ -31,6 +31,19 @@ func ParseConfig(path string, debug bool) ([]byte, error) { return ParseConfigContent(string(content), debug, nil, false) } +func ParseConfigContentToOptions(contentstr string, debug bool, configOpt *HiddifyOptions, fullConfig bool) (*option.Options, error) { + content, err := ParseConfigContent(contentstr, debug, configOpt, fullConfig) + if err != nil { + return nil, err + } + var options option.Options + err = json.Unmarshal(content, &options) + if err != nil { + return nil, err + } + return &options, nil +} + func ParseConfigContent(contentstr string, debug bool, configOpt *HiddifyOptions, fullConfig bool) ([]byte, error) { if configOpt == nil { configOpt = DefaultHiddifyOptions() diff --git a/config/warp.go b/config/warp.go index 892a46d4..abd27727 100644 --- a/config/warp.go +++ b/config/warp.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/bepass-org/warp-plus/warp" + "github.com/hiddify/hiddify-core/common" C "github.com/sagernet/sing-box/constant" // "github.com/bepass-org/wireguard-go/warp" @@ -189,8 +190,10 @@ func patchWarp(base *option.Outbound, configOpt *HiddifyOptions, final bool, sta rndDomain := strings.ToLower(generateRandomString(20)) staticIpsDns[rndDomain] = []string{} if host != "auto4" { - randomIpPort, _ := warp.RandomWarpEndpoint(false, true) - staticIpsDns[rndDomain] = append(staticIpsDns[rndDomain], randomIpPort.Addr().String()) + if host == "auto6" || common.CanConnectIPv6() { + randomIpPort, _ := warp.RandomWarpEndpoint(false, true) + staticIpsDns[rndDomain] = append(staticIpsDns[rndDomain], randomIpPort.Addr().String()) + } } if host != "auto6" { randomIpPort, _ := warp.RandomWarpEndpoint(true, false) diff --git a/extension/extension.go b/extension/extension.go index 55b90fa9..66d7baef 100644 --- a/extension/extension.go +++ b/extension/extension.go @@ -1,54 +1,72 @@ package extension import ( - "fmt" - "log" - - "github.com/hiddify/hiddify-core/extension/ui_elements" + "github.com/hiddify/hiddify-core/common" + "github.com/hiddify/hiddify-core/config" + "github.com/hiddify/hiddify-core/extension/ui" pb "github.com/hiddify/hiddify-core/hiddifyrpc" -) - -var ( - extensionsMap = make(map[string]*Extension) - extensionStatusMap = make(map[string]bool) + "github.com/jellydator/validation" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" ) type Extension interface { - GetTitle() string - GetDescription() string - GetUI() ui_elements.Form + GetUI() ui.Form SubmitData(data map[string]string) error Cancel() error Stop() error - UpdateUI(form ui_elements.Form) error + UpdateUI(form ui.Form) error + + BeforeAppConnect(hiddifySettings *config.HiddifyOptions, singconfig *option.Options) error + + StoreData() + init(id string) getQueue() chan *pb.ExtensionResponse getId() string } -type BaseExtension struct { +type Base[T any] struct { id string // responseStream grpc.ServerStreamingServer[pb.ExtensionResponse] queue chan *pb.ExtensionResponse + Data T } -// func (b *BaseExtension) mustEmbdedBaseExtension() { +// func (b *Base) mustEmbdedBaseExtension() { // } -func (b *BaseExtension) init(id string) { +func (b *Base[T]) BeforeAppConnect(hiddifySettings *config.HiddifyOptions, singconfig *option.Options) error { + return nil +} + +func (b *Base[T]) StoreData() { + common.Storage.SaveExtensionData(b.id, &b.Data) +} + +func (b *Base[T]) init(id string) { b.id = id b.queue = make(chan *pb.ExtensionResponse, 1) + common.Storage.GetExtensionData(b.id, &b.Data) } -func (b *BaseExtension) getQueue() chan *pb.ExtensionResponse { +func (b *Base[T]) getQueue() chan *pb.ExtensionResponse { return b.queue } -func (b *BaseExtension) getId() string { +func (b *Base[T]) getId() string { return b.id } -func (p *BaseExtension) UpdateUI(form ui_elements.Form) error { +func (e *Base[T]) ShowMessage(title string, msg string) error { + return e.ShowDialog(ui.Form{ + Title: title, + Description: msg, + Buttons: []string{ui.Button_Ok}, + }) +} + +func (p *Base[T]) UpdateUI(form ui.Form) error { p.queue <- &pb.ExtensionResponse{ ExtensionId: p.id, Type: pb.ExtensionResponseType_UPDATE_UI, @@ -57,7 +75,7 @@ func (p *BaseExtension) UpdateUI(form ui_elements.Form) error { return nil } -func (p *BaseExtension) ShowDialog(form ui_elements.Form) error { +func (p *Base[T]) ShowDialog(form ui.Form) error { p.queue <- &pb.ExtensionResponse{ ExtensionId: p.id, Type: pb.ExtensionResponseType_SHOW_DIALOG, @@ -67,20 +85,22 @@ func (p *BaseExtension) ShowDialog(form ui_elements.Form) error { return nil } -func RegisterExtension(id string, extension Extension) error { - if _, ok := extensionsMap[id]; ok { - err := fmt.Errorf("Extension with ID %s already exists", id) - log.Fatal(err) - return err +func (base *Base[T]) ValName(fieldPtr interface{}) string { + val, err := validation.ErrorFieldName(&base.Data, fieldPtr) + if err != nil { + log.Warn(err) + return "" } - if val, ok := extensionStatusMap[id]; ok && !val { - err := fmt.Errorf("Extension with ID %s is not enabled", id) - log.Fatal(err) - return err + if val == "" { + log.Warn("Field not found") + return "" } - extension.init(id) + return val +} - fmt.Printf("Registered extension: %+v\n", extension) - extensionsMap[id] = &extension - return nil +type ExtensionFactory struct { + Id string + Title string + Description string + Builder func() Extension } diff --git a/extension/extension_host.go b/extension/extension_host.go index 09437f97..31fbbe40 100644 --- a/extension/extension_host.go +++ b/extension/extension_host.go @@ -5,6 +5,7 @@ import ( "fmt" "log" + "github.com/hiddify/hiddify-core/common" pb "github.com/hiddify/hiddify-core/hiddifyrpc" "google.golang.org/grpc" ) @@ -18,11 +19,12 @@ func (ExtensionHostService) ListExtensions(ctx context.Context, empty *pb.Empty) Extensions: make([]*pb.Extension, 0), } - for _, extension := range extensionsMap { + for _, extension := range allExtensionsMap { extensionList.Extensions = append(extensionList.Extensions, &pb.Extension{ - Id: (*extension).getId(), - Title: (*extension).GetTitle(), - Description: (*extension).GetDescription(), + Id: extension.Id, + Title: extension.Title, + Description: extension.Description, + Enable: generalExtensionData.ExtensionStatusMap[extension.Id], }) } return extensionList, nil @@ -30,7 +32,7 @@ func (ExtensionHostService) ListExtensions(ctx context.Context, empty *pb.Empty) func (e ExtensionHostService) Connect(req *pb.ExtensionRequest, stream grpc.ServerStreamingServer[pb.ExtensionResponse]) error { // Get the extension from the map using the Extension ID - if extension, ok := extensionsMap[req.GetExtensionId()]; ok { + if extension, ok := enabledExtensionsMap[req.GetExtensionId()]; ok { log.Printf("Connecting stream for extension %s", req.GetExtensionId()) log.Printf("Extension data: %+v", extension) @@ -100,7 +102,7 @@ func (e ExtensionHostService) Connect(req *pb.ExtensionRequest, stream grpc.Serv } func (e ExtensionHostService) SubmitForm(ctx context.Context, req *pb.ExtensionRequest) (*pb.ExtensionActionResult, error) { - if extension, ok := extensionsMap[req.GetExtensionId()]; ok { + if extension, ok := enabledExtensionsMap[req.GetExtensionId()]; ok { (*extension).SubmitData(req.GetData()) return &pb.ExtensionActionResult{ @@ -113,7 +115,7 @@ func (e ExtensionHostService) SubmitForm(ctx context.Context, req *pb.ExtensionR } func (e ExtensionHostService) Cancel(ctx context.Context, req *pb.ExtensionRequest) (*pb.ExtensionActionResult, error) { - if extension, ok := extensionsMap[req.GetExtensionId()]; ok { + if extension, ok := enabledExtensionsMap[req.GetExtensionId()]; ok { (*extension).Cancel() return &pb.ExtensionActionResult{ @@ -126,9 +128,9 @@ func (e ExtensionHostService) Cancel(ctx context.Context, req *pb.ExtensionReque } func (e ExtensionHostService) Stop(ctx context.Context, req *pb.ExtensionRequest) (*pb.ExtensionActionResult, error) { - if extension, ok := extensionsMap[req.GetExtensionId()]; ok { + if extension, ok := enabledExtensionsMap[req.GetExtensionId()]; ok { (*extension).Stop() - + (*extension).StoreData() return &pb.ExtensionActionResult{ ExtensionId: req.ExtensionId, Code: pb.ResponseCode_OK, @@ -137,3 +139,24 @@ func (e ExtensionHostService) Stop(ctx context.Context, req *pb.ExtensionRequest } return nil, fmt.Errorf("Extension with ID %s not found", req.GetExtensionId()) } + +func (e ExtensionHostService) EditExtension(ctx context.Context, req *pb.EditExtensionRequest) (*pb.ExtensionActionResult, error) { + generalExtensionData.ExtensionStatusMap[req.GetExtensionId()] = req.Enable + if !req.Enable { + ext := *enabledExtensionsMap[req.GetExtensionId()] + if ext != nil { + ext.Stop() + ext.StoreData() + } + delete(enabledExtensionsMap, req.GetExtensionId()) + } else { + loadExtension(allExtensionsMap[req.GetExtensionId()]) + } + common.Storage.SaveExtensionData("default", generalExtensionData) + + return &pb.ExtensionActionResult{ + ExtensionId: req.ExtensionId, + Code: pb.ResponseCode_OK, + Message: "Success", + }, nil +} diff --git a/extension/html/a.js b/extension/html/a.js new file mode 100644 index 00000000..c161e8cb --- /dev/null +++ b/extension/html/a.js @@ -0,0 +1,12 @@ + +import * as a from "./rpc/extension_grpc_web_pb.js"; +const client = new ExtensionHostServiceClient('http://localhost:8080'); +const request = new GetHelloRequest(); +export const getHello = (name) => { + request.setName(name) +client.getHello(request, {}, (err, response) => { + console.log(request.getName()); + console.log(response.toObject()); + }); +} +getHello("D") \ No newline at end of file diff --git a/extension/html/index.html b/extension/html/index.html index 431c3370..50052079 100644 --- a/extension/html/index.html +++ b/extension/html/index.html @@ -8,18 +8,49 @@