feat: 初始提交 - Code Server Bridge完整实现

- OAuth认证系统(Gitea + Lua扩展)
- Git自动化操作(本地/SSH远程)
- 实时进度WebSocket推送
- 现代化Tab界面UI
- Cobra CLI命令行(init/version/serve)
- 完整构建系统(Makefile + Taskfile)
- UPX压缩支持(体积减少70%)
This commit is contained in:
2026-01-08 23:32:29 +08:00
commit 8265df0dcd
40 changed files with 3786 additions and 0 deletions

307
pkg/lua/engine.go Normal file
View File

@@ -0,0 +1,307 @@
package lua
import (
"errors"
"fmt"
"reflect"
lua "github.com/yuin/gopher-lua"
)
type Engine struct {
L *lua.LState
}
func New() *Engine {
L := lua.NewState()
return &Engine{L: L}
}
func (e *Engine) Close() {
e.L.Close()
}
// RegisterAPI registers all Lua API modules (http, json, log, etc.)
func (e *Engine) RegisterAPI() {
RegisterHTTPModule(e.L)
RegisterJSONModule(e.L)
RegisterLogModule(e.L)
RegisterUtilModule(e.L)
}
func (e *Engine) LoadFile(path string) error {
return e.L.DoFile(path)
}
func (e *Engine) CallString(fn string, args ...lua.LValue) (string, error) {
L := e.L
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal(fn),
NRet: 1,
Protect: true,
}, args...); err != nil {
return "", err
}
ret := L.Get(-1)
L.Pop(1)
return ret.String(), nil
}
func (e *Engine) CallStruct(fn string, out any, args ...lua.LValue) error {
L := e.L
f := L.GetGlobal(fn)
if f.Type() != lua.LTFunction {
return fmt.Errorf("lua function %s not found", fn)
}
if err := L.CallByParam(lua.P{
Fn: f,
NRet: 1,
Protect: true,
}, args...); err != nil {
return err
}
ret := L.Get(-1)
L.Pop(1)
table, ok := ret.(*lua.LTable)
if !ok {
return fmt.Errorf("lua function %s must return table", fn)
}
return luaTableToStruct(table, out)
}
func luaTableToStruct(t *lua.LTable, out any) error {
v := reflect.ValueOf(out)
if v.Kind() != reflect.Ptr {
return errors.New("out must be pointer")
}
v = v.Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Type().Field(i)
key := field.Tag.Get("lua")
if key == "" {
continue
}
lv := t.RawGetString(key)
if lv == lua.LNil {
continue
}
fv := v.Field(i)
if err := setFieldFromLua(fv, lv); err != nil {
return fmt.Errorf("field %s: %w", field.Name, err)
}
}
return nil
}
// setFieldFromLua sets a reflect.Value field from a lua.LValue
func setFieldFromLua(fv reflect.Value, lv lua.LValue) error {
if !fv.CanSet() {
return errors.New("field cannot be set")
}
switch fv.Kind() {
case reflect.String:
fv.SetString(lv.String())
case reflect.Bool:
fv.SetBool(lua.LVAsBool(lv))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if n, ok := lv.(lua.LNumber); ok {
fv.SetInt(int64(n))
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if n, ok := lv.(lua.LNumber); ok {
fv.SetUint(uint64(n))
}
case reflect.Float32, reflect.Float64:
if n, ok := lv.(lua.LNumber); ok {
fv.SetFloat(float64(n))
}
case reflect.Slice:
if ltable, ok := lv.(*lua.LTable); ok {
return setSliceFromLuaTable(fv, ltable)
}
case reflect.Map:
if ltable, ok := lv.(*lua.LTable); ok {
return setMapFromLuaTable(fv, ltable)
}
case reflect.Struct:
if ltable, ok := lv.(*lua.LTable); ok {
return luaTableToStruct(ltable, fv.Addr().Interface())
}
case reflect.Ptr:
if fv.IsNil() {
fv.Set(reflect.New(fv.Type().Elem()))
}
return setFieldFromLua(fv.Elem(), lv)
}
return nil
}
// setSliceFromLuaTable converts a Lua table to a Go slice
func setSliceFromLuaTable(fv reflect.Value, t *lua.LTable) error {
length := t.Len()
slice := reflect.MakeSlice(fv.Type(), length, length)
for i := 1; i <= length; i++ {
lv := t.RawGetInt(i)
if lv == lua.LNil {
continue
}
elem := slice.Index(i - 1)
if err := setFieldFromLua(elem, lv); err != nil {
return fmt.Errorf("index %d: %w", i, err)
}
}
fv.Set(slice)
return nil
}
// setMapFromLuaTable converts a Lua table to a Go map
func setMapFromLuaTable(fv reflect.Value, t *lua.LTable) error {
mapType := fv.Type()
newMap := reflect.MakeMap(mapType)
var convErr error
t.ForEach(func(key, value lua.LValue) {
if convErr != nil {
return
}
// Convert key
k := reflect.New(mapType.Key()).Elem()
if err := setFieldFromLua(k, key); err != nil {
convErr = fmt.Errorf("key conversion: %w", err)
return
}
// Convert value
v := reflect.New(mapType.Elem()).Elem()
if err := setFieldFromLua(v, value); err != nil {
convErr = fmt.Errorf("value conversion: %w", err)
return
}
newMap.SetMapIndex(k, v)
})
if convErr != nil {
return convErr
}
fv.Set(newMap)
return nil
}
// GoToLua converts a Go value to a Lua value
func GoToLua(L *lua.LState, v any) lua.LValue {
if v == nil {
return lua.LNil
}
val := reflect.ValueOf(v)
return goValueToLua(L, val)
}
// goValueToLua converts a reflect.Value to lua.LValue
func goValueToLua(L *lua.LState, v reflect.Value) lua.LValue {
if !v.IsValid() {
return lua.LNil
}
// Dereference pointers and interfaces
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
if v.IsNil() {
return lua.LNil
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Bool:
return lua.LBool(v.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return lua.LNumber(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return lua.LNumber(v.Uint())
case reflect.Float32, reflect.Float64:
return lua.LNumber(v.Float())
case reflect.String:
return lua.LString(v.String())
case reflect.Slice, reflect.Array:
table := L.NewTable()
for i := 0; i < v.Len(); i++ {
table.Append(goValueToLua(L, v.Index(i)))
}
return table
case reflect.Map:
table := L.NewTable()
iter := v.MapRange()
for iter.Next() {
key := iter.Key()
val := goValueToLua(L, iter.Value())
// For string keys, use RawSetString for better Lua compatibility
if key.Kind() == reflect.String {
table.RawSetString(key.String(), val)
} else {
// For non-string keys, convert and use RawSet
luaKey := goValueToLua(L, key)
table.RawSet(luaKey, val)
}
}
return table
case reflect.Struct:
table := L.NewTable()
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
// Skip unexported fields
if !field.IsExported() {
continue
}
// Get lua tag or use field name
luaKey := field.Tag.Get("lua")
if luaKey == "" {
luaKey = field.Name
}
fieldValue := goValueToLua(L, v.Field(i))
table.RawSetString(luaKey, fieldValue)
}
return table
default:
return lua.LNil
}
}