Files
transfer-site/pkg/lua/api.go
zhilv 8265df0dcd feat: 初始提交 - Code Server Bridge完整实现
- OAuth认证系统(Gitea + Lua扩展)
- Git自动化操作(本地/SSH远程)
- 实时进度WebSocket推送
- 现代化Tab界面UI
- Cobra CLI命令行(init/version/serve)
- 完整构建系统(Makefile + Taskfile)
- UPX压缩支持(体积减少70%)
2026-01-08 23:32:29 +08:00

394 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package lua
import (
"cs-bridge/pkg/httpclient"
"cs-bridge/pkg/logger"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/url"
lua "github.com/yuin/gopher-lua"
"resty.dev/v3"
)
// RegisterHTTPModule registers the http module in Lua
func RegisterHTTPModule(L *lua.LState) {
httpMod := L.NewTable()
// http.get(url, headers)
httpMod.RawSetString("get", L.NewFunction(luaHTTPGet))
// http.post(url, data, headers)
httpMod.RawSetString("post", L.NewFunction(luaHTTPPost))
// http.put(url, data, headers)
httpMod.RawSetString("put", L.NewFunction(luaHTTPPut))
// http.delete(url, headers)
httpMod.RawSetString("delete", L.NewFunction(luaHTTPDelete))
L.SetGlobal("http", httpMod)
}
// luaHTTPGet implements http.get(url, headers)
func luaHTTPGet(L *lua.LState) int {
url := L.CheckString(1)
headers := L.OptTable(2, nil)
req := httpclient.Default.R()
// Set headers if provided
if headers != nil {
headers.ForEach(func(key, value lua.LValue) {
req.SetHeader(key.String(), value.String())
})
}
resp, err := req.Get(url)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
result := parseHTTPResponse(L, resp)
L.Push(result)
return 1
}
// luaHTTPPost implements http.post(url, data, headers)
func luaHTTPPost(L *lua.LState) int {
url := L.CheckString(1)
data := L.CheckTable(2)
headers := L.OptTable(3, nil)
req := httpclient.Default.R()
// Convert Lua table to map for request body
bodyMap := luaTableToMap(data)
req.SetBody(bodyMap)
req.SetHeader("Content-Type", "application/json")
// Set additional headers if provided
if headers != nil {
headers.ForEach(func(key, value lua.LValue) {
req.SetHeader(key.String(), value.String())
})
}
resp, err := req.Post(url)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
result := parseHTTPResponse(L, resp)
L.Push(result)
return 1
}
// luaHTTPPut implements http.put(url, data, headers)
func luaHTTPPut(L *lua.LState) int {
url := L.CheckString(1)
data := L.CheckTable(2)
headers := L.OptTable(3, nil)
req := httpclient.Default.R()
bodyMap := luaTableToMap(data)
req.SetBody(bodyMap)
req.SetHeader("Content-Type", "application/json")
if headers != nil {
headers.ForEach(func(key, value lua.LValue) {
req.SetHeader(key.String(), value.String())
})
}
resp, err := req.Put(url)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
result := parseHTTPResponse(L, resp)
L.Push(result)
return 1
}
// luaHTTPDelete implements http.delete(url, headers)
func luaHTTPDelete(L *lua.LState) int {
url := L.CheckString(1)
headers := L.OptTable(2, nil)
req := httpclient.Default.R()
if headers != nil {
headers.ForEach(func(key, value lua.LValue) {
req.SetHeader(key.String(), value.String())
})
}
resp, err := req.Delete(url)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
result := parseHTTPResponse(L, resp)
L.Push(result)
return 1
}
// parseHTTPResponse parses HTTP response and returns Lua table
func parseHTTPResponse(L *lua.LState, resp *resty.Response) lua.LValue {
// Read response body
body, err := io.ReadAll(resp.Body)
if err != nil {
logger.GetLogger().Error(fmt.Sprintf("读取响应体失败: %v", err))
// Return error table
result := L.NewTable()
result.RawSetString("error", lua.LString(err.Error()))
result.RawSetString("status", lua.LNumber(resp.StatusCode()))
return result
}
defer resp.Body.Close()
// Log response for debugging
logger.GetLogger().Debug(fmt.Sprintf("HTTP响应 [%d]: %s", resp.StatusCode(), string(body)))
// Try to parse as JSON first
var jsonData any
if err := json.Unmarshal(body, &jsonData); err == nil {
logger.GetLogger().Debug(fmt.Sprintf("JSON解析成功类型: %T, 值: %+v", jsonData, jsonData))
result := GoToLua(L, jsonData)
logger.GetLogger().Debug(fmt.Sprintf("转换为Lua后类型: %s", result.Type().String()))
// Try to access a test field if it's a table
if tbl, ok := result.(*lua.LTable); ok {
testVal := tbl.RawGetString("access_token")
logger.GetLogger().Debug(fmt.Sprintf("测试访问access_token: %s (type: %s)", testVal.String(), testVal.Type().String()))
}
return result
}
// If not JSON, return as string in a table
logger.GetLogger().Debug(fmt.Sprintf("响应不是有效JSON: %v", err))
result := L.NewTable()
result.RawSetString("body", lua.LString(string(body)))
result.RawSetString("status", lua.LNumber(resp.StatusCode()))
return result
}
// luaTableToMap converts a Lua table to a Go map
func luaTableToMap(t *lua.LTable) map[string]any {
result := make(map[string]any)
t.ForEach(func(key, value lua.LValue) {
result[key.String()] = luaValueToGo(value)
})
return result
}
// luaValueToGo converts a Lua value to a Go value
func luaValueToGo(lv lua.LValue) any {
switch v := lv.(type) {
case *lua.LNilType:
return nil
case lua.LBool:
return bool(v)
case lua.LNumber:
return float64(v)
case lua.LString:
return string(v)
case *lua.LTable:
// Check if it's an array or map
if v.Len() > 0 {
// Array
arr := make([]any, 0, v.Len())
for i := 1; i <= v.Len(); i++ {
arr = append(arr, luaValueToGo(v.RawGetInt(i)))
}
return arr
}
// Map
m := make(map[string]any)
v.ForEach(func(key, value lua.LValue) {
m[key.String()] = luaValueToGo(value)
})
return m
default:
return nil
}
}
// RegisterJSONModule registers the json module in Lua
func RegisterJSONModule(L *lua.LState) {
jsonMod := L.NewTable()
// json.encode(table)
jsonMod.RawSetString("encode", L.NewFunction(luaJSONEncode))
// json.decode(string)
jsonMod.RawSetString("decode", L.NewFunction(luaJSONDecode))
L.SetGlobal("json", jsonMod)
}
// luaJSONEncode implements json.encode(value)
func luaJSONEncode(L *lua.LState) int {
value := L.CheckAny(1)
goValue := luaValueToGo(value)
jsonBytes, err := json.Marshal(goValue)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(string(jsonBytes)))
return 1
}
// luaJSONDecode implements json.decode(jsonString)
func luaJSONDecode(L *lua.LState) int {
jsonStr := L.CheckString(1)
var data any
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(GoToLua(L, data))
return 1
}
// RegisterLogModule registers the log module in Lua
func RegisterLogModule(L *lua.LState) {
logMod := L.NewTable()
logMod.RawSetString("debug", L.NewFunction(luaLogDebug))
logMod.RawSetString("info", L.NewFunction(luaLogInfo))
logMod.RawSetString("warn", L.NewFunction(luaLogWarn))
logMod.RawSetString("error", L.NewFunction(luaLogError))
L.SetGlobal("log", logMod)
}
// luaLogDebug implements log.debug(message, ...)
func luaLogDebug(L *lua.LState) int {
msg := formatLogMessage(L)
logger.GetLogger().Debug(msg)
return 0
}
// luaLogInfo implements log.info(message, ...)
func luaLogInfo(L *lua.LState) int {
msg := formatLogMessage(L)
logger.GetLogger().Info(msg)
return 0
}
// luaLogWarn implements log.warn(message, ...)
func luaLogWarn(L *lua.LState) int {
msg := formatLogMessage(L)
logger.GetLogger().Warn(msg)
return 0
}
// luaLogError implements log.error(message, ...)
func luaLogError(L *lua.LState) int {
msg := formatLogMessage(L)
logger.GetLogger().Error(msg)
return 0
}
// formatLogMessage formats log message from Lua arguments
func formatLogMessage(L *lua.LState) string {
n := L.GetTop()
if n == 0 {
return ""
}
if n == 1 {
return L.CheckString(1)
}
// Format string with arguments
format := L.CheckString(1)
args := make([]any, n-1)
for i := 2; i <= n; i++ {
args[i-2] = luaValueToGo(L.Get(i))
}
return fmt.Sprintf(format, args...)
}
// RegisterUtilModule registers utility functions in Lua
func RegisterUtilModule(L *lua.LState) {
utilMod := L.NewTable()
// base64.encode(string)
utilMod.RawSetString("base64_encode", L.NewFunction(luaBase64Encode))
// base64.decode(string)
utilMod.RawSetString("base64_decode", L.NewFunction(luaBase64Decode))
// url.encode(string)
utilMod.RawSetString("url_encode", L.NewFunction(luaURLEncode))
// url.decode(string)
utilMod.RawSetString("url_decode", L.NewFunction(luaURLDecode))
L.SetGlobal("util", utilMod)
}
// luaBase64Encode implements base64_encode(str)
func luaBase64Encode(L *lua.LState) int {
str := L.CheckString(1)
encoded := base64.StdEncoding.EncodeToString([]byte(str))
L.Push(lua.LString(encoded))
return 1
}
// luaBase64Decode implements base64_decode(str)
func luaBase64Decode(L *lua.LState) int {
str := L.CheckString(1)
decoded, err := base64.StdEncoding.DecodeString(str)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(string(decoded)))
return 1
}
// luaURLEncode implements url_encode(str)
func luaURLEncode(L *lua.LState) int {
str := L.CheckString(1)
encoded := url.QueryEscape(str)
L.Push(lua.LString(encoded))
return 1
}
// luaURLDecode implements url_decode(str)
func luaURLDecode(L *lua.LState) int {
str := L.CheckString(1)
decoded, err := url.QueryUnescape(str)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(decoded))
return 1
}