feat: support calling Gopeed API via host RPC (#1247)

This commit is contained in:
Levi authored and GitHub committed 2026-01-21 13:21:20 +08:00
1 parent 6016ee1d24
commit d90af0b9d1
4 files changed
+100 -17

No files matched your search

+54 -14
View File
@@ -60,10 +60,39 @@ func wakeup(hidden bool) error {
return fmt.Errorf("start gopeed failed")
}
// postToFlutter sends a POST request to Flutter RPC server
func postToFlutter(path string, body []byte, headers map[string]string, timeout time.Duration) (*http.Response, error) {
client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return Dial()
},
},
Timeout: timeout,
}
req, err := http.NewRequest("POST", "http://127.0.0.1"+path, bytes.NewBuffer(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
for k, v := range headers {
req.Header.Set(k, v)
}
return client.Do(req)
}
var apiMap = map[string]func(message *Message) (data any, err error){
"ping": func(message *Message) (data any, err error) {
return check()
},
"wakeup": func(message *Message) (data any, err error) {
silent := false
if v, ok := message.Meta["silent"]; ok {
silent, _ = v.(bool)
}
err = wakeup(silent)
return
},
"create": func(message *Message) (data any, err error) {
buf, err := message.Params.MarshalJSON()
if err != nil {
@@ -79,24 +108,36 @@ var apiMap = map[string]func(message *Message) (data any, err error){
return nil, err
}
client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return Dial()
},
},
Timeout: 10 * time.Second,
headers := make(map[string]string)
if message.Meta != nil {
metaJson, _ := json.Marshal(message.Meta)
headers["X-Gopeed-Host-Meta"] = string(metaJson)
}
req, err := http.NewRequest("POST", "http://127.0.0.1/create", bytes.NewBuffer(buf))
_, err = postToFlutter("/create", buf, headers, 10*time.Second)
return
},
"forward": func(message *Message) (data any, err error) {
buf, err := message.Params.MarshalJSON()
if err != nil {
return
}
if message.Meta != nil {
metaJson, _ := json.Marshal(message.Meta)
req.Header.Set("X-Gopeed-Host-Meta", string(metaJson))
resp, err := postToFlutter("/forward", buf, nil, 60*time.Second)
if err != nil {
return
}
_, err = client.Do(req)
return
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var respData map[string]json.RawMessage
if err := json.Unmarshal(respBody, &respData); err != nil {
return nil, err
}
return respData, nil
},
}
@@ -108,7 +149,6 @@ func main() {
var length uint32
if err := binary.Read(os.Stdin, binary.NativeEndian, &length); err != nil {
if err == io.EOF {
// Connection closed by Chrome
return
}
sendError("Failed to read message length: " + err.Error())
+16 -1
View File
@@ -14,7 +14,6 @@ import 'model/downloader_config.dart';
import 'model/extension.dart';
import 'model/install_extension.dart';
import 'model/login.dart';
import 'model/request.dart';
import 'model/resolve_result.dart';
import 'model/resolve_task.dart';
import 'model/result.dart';
@@ -267,3 +266,19 @@ String join(String path) {
: baseUrl;
return "$cleanBaseUrl/${Util.cleanPath(path)}";
}
/// Generic request method for API proxy
/// Directly forwards requests to gopeed REST API
Future<Response> forward(
String path, {
String method = 'GET',
dynamic data,
Map<String, dynamic>? queryParameters,
}) async {
return _client.dio.request(
path,
data: data,
queryParameters: queryParameters,
options: Options(method: method),
);
}
@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:ui';
import 'package:app_links/app_links.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:get/get.dart';
@@ -20,6 +21,7 @@ import '../../../../api/api.dart';
import '../../../../api/model/create_task.dart';
import '../../../../api/model/downloader_config.dart';
import '../../../../api/model/request.dart';
import '../../../../api/model/result.dart';
import '../../../../core/common/start_config.dart';
import '../../../../core/libgopeed_boot.dart';
import '../../../../database/database.dart';
@@ -304,6 +306,33 @@ class AppController extends GetxController with WindowListener, TrayListener {
}
}
},
"/forward": (ctx) async {
try {
final body = await ctx.readJSON();
final method = (body['method'] as String?)?.toUpperCase() ?? 'GET';
final path = (body['path'] as String?) ?? "/";
final data = body['data'];
final query = body['query'] as Map<String, dynamic>?;
// Forward request to gopeed REST API
final response = await forward(
path,
method: method,
data: data,
queryParameters: query,
);
// Return raw response
await ctx.writeJSON(response.data);
} catch (e) {
if (e is DioException && e.response != null) {
// Return API error response
await ctx.writeJSON(e.response!.data);
} else {
await ctx.writeJSON(Result(code: 1, msg: e.toString()).toJson());
}
}
},
});
} catch (e) {
logger.w("start rpc server fail", e, StackTrace.current);
+1 -2
View File
@@ -1,8 +1,7 @@
import 'dart:io';
import 'dart:convert';
import 'dart:io';
import 'package:dart_ipc/dart_ipc.dart';
import 'package:path_provider/path_provider.dart';
import '../../util/util.dart';