Browse Source

(jsrt) fix: `fetch`

lollipopkit🏳️‍⚧️ 5 months ago
parent
commit
5ffdd9f542
3 changed files with 160 additions and 69 deletions
  1. 28 41
      middleware/jsrt/fetch.go
  2. 1 0
      middleware/jsrt/jsrt.go
  3. 131 28
      scripts/pre_process.js

+ 28 - 41
middleware/jsrt/fetch.go

@@ -1,6 +1,7 @@
 package jsrt
 
 import (
+	"bytes"
 	"context"
 	"encoding/json"
 	"fmt"
@@ -14,16 +15,15 @@ type JSFetchRequest struct {
 	Method  string            `json:"method"`
 	URL     string            `json:"url"`
 	Headers map[string]string `json:"headers"`
-	Body    string            `json:"body"`
+	Body    any               `json:"body"`
 	Timeout int               `json:"timeout"`
 }
 
 type JSFetchResponse struct {
-	Status     int               `json:"status"`
-	StatusText string            `json:"statusText"`
-	Headers    map[string]string `json:"headers"`
-	Body       string            `json:"body"`
-	OK         bool              `json:"ok"`
+	Status  int               `json:"status"`
+	Headers map[string]string `json:"headers"`
+	Body    string            `json:"body"`
+	Error   string            `json:"error,omitempty"`
 }
 
 func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
@@ -54,17 +54,7 @@ func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
 			}
 
 			if body, exists := optMap["body"]; exists {
-				switch v := body.(type) {
-				case string:
-					req.Body = v
-				case map[string]any:
-					if bodyBytes, err := json.Marshal(v); err == nil {
-						req.Body = string(bodyBytes)
-						req.Headers["Content-Type"] = "application/json"
-					}
-				default:
-					req.Body = fmt.Sprintf("%v", body)
-				}
+				req.Body = body
 			}
 
 			if timeout, exists := optMap["timeout"]; exists {
@@ -77,18 +67,27 @@ func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
 
 	// 创建HTTP请求
 	var bodyReader io.Reader
-	if req.Body != "" {
-		bodyReader = strings.NewReader(req.Body)
+	switch body := req.Body.(type) {
+	case string:
+		bodyReader = strings.NewReader(body)
+	case []byte:
+		bodyReader = bytes.NewReader(body)
+	case nil:
+		bodyReader = nil
+	default:
+		bodyBytes, err := json.Marshal(body)
+		if err != nil {
+			return &JSFetchResponse{
+				Error: fmt.Sprintf("Failed to marshal body: %v", err),
+			}
+		}
+		bodyReader = bytes.NewReader(bodyBytes)
 	}
 
 	httpReq, err := http.NewRequest(req.Method, req.URL, bodyReader)
 	if err != nil {
 		return &JSFetchResponse{
-			Status:     0,
-			StatusText: err.Error(),
-			Headers:    make(map[string]string),
-			Body:       "",
-			OK:         false,
+			Error: err.Error(),
 		}
 	}
 
@@ -110,13 +109,7 @@ func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
 	// 执行请求
 	resp, err := p.httpClient.Do(httpReq)
 	if err != nil {
-		return &JSFetchResponse{
-			Status:     0,
-			StatusText: err.Error(),
-			Headers:    make(map[string]string),
-			Body:       "",
-			OK:         false,
-		}
+		return &JSFetchResponse{}
 	}
 	defer resp.Body.Close()
 
@@ -124,11 +117,7 @@ func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
 	bodyBytes, err := io.ReadAll(resp.Body)
 	if err != nil {
 		return &JSFetchResponse{
-			Status:     resp.StatusCode,
-			StatusText: resp.Status,
-			Headers:    make(map[string]string),
-			Body:       "",
-			OK:         resp.StatusCode >= 200 && resp.StatusCode < 300,
+			Status: resp.StatusCode,
 		}
 	}
 
@@ -141,10 +130,8 @@ func (p *JSRuntimePool) fetch(url string, options ...any) *JSFetchResponse {
 	}
 
 	return &JSFetchResponse{
-		Status:     resp.StatusCode,
-		StatusText: resp.Status,
-		Headers:    headers,
-		Body:       string(bodyBytes),
-		OK:         resp.StatusCode >= 200 && resp.StatusCode < 300,
+		Status:  resp.StatusCode,
+		Headers: headers,
+		Body:    string(bodyBytes),
 	}
 }

+ 1 - 0
middleware/jsrt/jsrt.go

@@ -146,6 +146,7 @@ func (p *JSRuntimePool) setupGlobals(vm *goja.Runtime) {
 
 	// 数据库
 	vm.Set("db", &JSDatabase{db: model.DB})
+	vm.Set("logdb", &JSDatabase{db: model.LOG_DB})
 
 	// 定时器
 	vm.Set("setTimeout", func(fn func(), delay int) {

+ 131 - 28
scripts/pre_process.js

@@ -30,34 +30,137 @@ function preProcessRequest(ctx) {
     // }
 
     // 例子:修改请求
-    if (ctx.URL.includes("/v1/chat/completions")) {
-        try {
-            var bodyObj = ctx.Body;
-
-            let firstMsg = {
-                role: "user",
-                content: "今天天气怎么样"
-            };
-            bodyObj.messages[0] = firstMsg;
-            console.log("Modified first message:", JSON.stringify(firstMsg));
-            console.log("Modified body:", JSON.stringify(bodyObj));
-
-            return {
-                body: bodyObj,
-                headers: {
-                    ...ctx.Headers,
-                    "X-Modified-Body": "true"
-                }
-            };
-        } catch (e) {
-            console.error("Failed to modify request body:", {
-                message: e.message,
-                stack: e.stack,
-                bodyType: typeof ctx.Body,
-                url: ctx.URL
-            });
-        }
-    }
+    // if (ctx.URL.includes("/v1/chat/completions")) {
+    //     try {
+    //         var bodyObj = ctx.Body;
+
+    //         let firstMsg = { // 需要新建一个对象,不能修改原有对象
+    //             role: "user",
+    //             content: "今天天气怎么样"
+    //         };
+    //         bodyObj.messages[0] = firstMsg;
+    //         console.log("Modified first message:", JSON.stringify(firstMsg));
+    //         console.log("Modified body:", JSON.stringify(bodyObj));
+
+    //         return {
+    //             body: bodyObj,
+    //             headers: {
+    //                 ...ctx.Headers,
+    //                 "X-Modified-Body": "true"
+    //             }
+    //         };
+    //     } catch (e) {
+    //         console.error("Failed to modify request body:", {
+    //             message: e.message,
+    //             stack: e.stack,
+    //             bodyType: typeof ctx.Body,
+    //             url: ctx.URL
+    //         });
+    //     }
+    // }
+
+    // // 例子:读取最近一条日志,新增 jsrt 日志,并输出日志总数
+    // if (ctx.URL) {
+    //     try {
+    //         // 1. 读取最近一条日志
+    //         var recentLogs = logdb.Query(
+    //             "SELECT id, user_id, username, content, created_at FROM logs ORDER BY id DESC LIMIT 1"
+    //         );
+
+    //         var recentLog = null;
+    //         if (recentLogs && recentLogs.length > 0) {
+    //             recentLog = recentLogs[0];
+    //             console.log("最近一条日志:", JSON.stringify(recentLog));
+    //         }
+
+    //         // 2. 新增一条 jsrt 日志
+    //         var currentTimestamp = Math.floor(Date.now() / 1000);
+    //         var jsrtLogContent = "JSRT 预处理中间件执行 - " + ctx.URL + " - " + new Date().toISOString();
+
+    //         var insertResult = logdb.Exec(
+    //             "INSERT INTO logs (user_id, username, created_at, type, content) VALUES (?, ?, ?, ?, ?)",
+    //             ctx.UserID || 0,
+    //             ctx.Username || "jsrt-system",
+    //             currentTimestamp,
+    //             4, // LogTypeSystem
+    //             jsrtLogContent
+    //         );
+
+    //         if (insertResult.error) {
+    //             console.error("插入 JSRT 日志失败:", insertResult.error);
+    //         } else {
+    //             console.log("成功插入 JSRT 日志,影响行数:", insertResult.rowsAffected);
+    //         }
+
+    //         // 3. 输出日志总数
+    //         var totalLogsResult = logdb.Query("SELECT COUNT(*) as total FROM logs");
+    //         var totalLogs = 0;
+    //         if (totalLogsResult && totalLogsResult.length > 0) {
+    //             totalLogs = totalLogsResult[0].total;
+    //         }
+
+    //         console.log("当前日志总数:", totalLogs);
+    //         console.log("JSRT 日志管理示例执行完成");
+
+    //     } catch (e) {
+    //         console.error("JSRT 日志管理示例执行失败:", {
+    //             message: e.message,
+    //             stack: e.stack,
+    //             url: ctx.URL
+    //         });
+    //     }
+    // }
+
+    // // 例子:使用 fetch 调用外部 API
+    // if (ctx.URL.includes("/api/uptime/status")) {
+    //     try {
+    //         // 使用 httpbin.org/ip 测试 fetch 功能
+    //         var response = fetch("https://httpbin.org/ip", {
+    //             method: "GET",
+    //             timeout: 5, // 5秒超时
+    //             headers: {
+    //                 "User-Agent": "OneAPI-JSRT/1.0"
+    //             }
+    //         });
+            
+    //         if (response.Error.length === 0) {
+    //             // 解析响应体
+    //             var ipData = JSON.parse(response.Body);
+                
+    //             // 可以根据获取到的 IP 信息进行后续处理
+    //             if (ipData.origin) {
+    //                 console.log("外部 IP 地址:", ipData.origin);
+                    
+    //                 // 示例:记录 IP 信息到数据库
+    //                 var currentTimestamp = Math.floor(Date.now() / 1000);
+    //                 var logContent = "Fetch 示例 - 外部 IP: " + ipData.origin + " - " + new Date().toISOString();
+                    
+    //                 var insertResult = logdb.Exec(
+    //                     "INSERT INTO logs (user_id, username, created_at, type, content) VALUES (?, ?, ?, ?, ?)",
+    //                     ctx.UserID || 0,
+    //                     ctx.Username || "jsrt-fetch",
+    //                     currentTimestamp,
+    //                     4, // LogTypeSystem
+    //                     logContent
+    //                 );
+                    
+    //                 if (insertResult.error) {
+    //                     console.error("记录 IP 信息失败:", insertResult.error);
+    //                 } else {
+    //                     console.log("成功记录 IP 信息到数据库");
+    //                 }
+    //             }
+    //         } else {
+    //             console.error("Fetch 失败 ", response.Status, " ", response.Error);
+    //         }
+    //     } catch (e) {
+    //         console.error("Fetch 失败:", {
+    //             message: e.message,
+    //             stack: e.stack,
+    //             url: ctx.URL
+    //         });
+    //     }
+    // }
 
     return undefined; // 跳过处理,继续执行下一个中间件或路由
 }