add gemini support

This commit is contained in:
2026-02-16 21:00:04 -05:00
parent 94818e25fe
commit 472e27b1fa
16 changed files with 465 additions and 129 deletions

View File

@@ -1,23 +1,19 @@
package server
import (
"errors"
"fes/modules/config"
"io/fs"
"os"
"path/filepath"
"strings"
lua "github.com/yuin/gopher-lua"
)
/* this is the request data we pass over the bus to the application, via the fes.bus interface */
type reqData struct {
path string
params map[string]string
}
/* declarative sets in the page declaration */
type DeclarativeSets struct {
protos struct {
http bool
@@ -25,7 +21,6 @@ type DeclarativeSets struct {
}
}
/* returns a string of rendered markup */
func render(luapath string, requestData reqData, setBuffer *DeclarativeSets) ([]byte, error) {
L := lua.NewState()
defer L.Close()
@@ -36,10 +31,8 @@ func render(luapath string, requestData reqData, setBuffer *DeclarativeSets) ([]
continue
}
path := filepath.Join("lib", de.Name())
if data, err := config.Lib.ReadFile(path); err != nil {
continue
} else {
L.DoString(string(data))
if data, err := config.Lib.ReadFile(path); err == nil {
_ = L.DoString(string(data))
}
}
}
@@ -50,10 +43,11 @@ func render(luapath string, requestData reqData, setBuffer *DeclarativeSets) ([]
if err != nil {
panic(err)
}
if err := L.DoString(string(fileData)); err != nil {
panic(err)
}
L.Push(L.Get(-1))
return 1
})
}
@@ -63,88 +57,40 @@ func render(luapath string, requestData reqData, setBuffer *DeclarativeSets) ([]
preloadLuaModule("lib.util", "lib/util.lua")
L.PreloadModule("fes", func(L *lua.LState) int {
mod := L.NewTable()
libModules := []string{}
if ents, err := fs.ReadDir(config.Lib, "lib"); err == nil {
for _, e := range ents {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".lua") {
continue
}
libModules = append(libModules, strings.TrimSuffix(e.Name(), ".lua"))
}
}
for _, modName := range libModules {
path := filepath.Join("lib", modName+".lua")
fileData, err := config.Lib.ReadFile(path)
if err != nil {
continue
}
if err := L.DoString(string(fileData)); err != nil {
continue
}
val := L.Get(-1)
L.Pop(1)
tbl, ok := val.(*lua.LTable)
if !ok || tbl == nil {
tbl = L.NewTable()
}
if modName == "fes" {
tbl.ForEach(func(k, v lua.LValue) { mod.RawSet(k, v) })
} else {
mod.RawSetString(modName, tbl)
}
fileData, err := config.Lib.ReadFile("lib/fes.lua")
if err != nil {
panic(err)
}
mod.RawSetString("app", func() *lua.LTable {
app := L.NewTable()
includeDir := "include"
if err := L.DoString(string(fileData)); err != nil {
panic(err)
}
includes, err := os.ReadDir(includeDir)
if err != nil {
return app // load no includes
}
mod := L.Get(-1)
L.Pop(1)
for _, de := range includes {
if de.IsDir() || !strings.HasSuffix(de.Name(), ".lua") {
continue
}
base := strings.TrimSuffix(de.Name(), ".lua")
path := filepath.Join(includeDir, de.Name())
if _, err := os.Stat(path); err != nil {
continue
} else if err := L.DoFile(path); err != nil {
continue
}
val := L.Get(-1)
L.Pop(1)
tbl, ok := val.(*lua.LTable)
if !ok || tbl == nil {
tbl = L.NewTable()
}
app.RawSetString(base, tbl)
}
return app
}())
tbl, ok := mod.(*lua.LTable)
if !ok {
panic("fes module did not return table")
}
bus := L.NewTable()
bus.RawSetString("url", lua.LString(requestData.path))
params := L.NewTable()
for k, v := range requestData.params {
params.RawSetString(k, lua.LString(v))
}
bus.RawSetString("params", params)
mod.RawSetString("bus", bus)
mod.RawSetString("markdown_to_html", L.NewFunction(func(L *lua.LState) int {
tbl.RawSetString("bus", bus)
tbl.RawSetString("markdown_to_html", L.NewFunction(func(L *lua.LState) int {
L.Push(lua.LString(markdownToHTML(L.ToString(1))))
return 1
}))
L.Push(mod)
L.Push(tbl)
return 1
})
@@ -156,48 +102,27 @@ func render(luapath string, requestData reqData, setBuffer *DeclarativeSets) ([]
return []byte(""), nil
}
L.SetGlobal("__fes_result", L.Get(-1))
if err := L.DoString("return tostring(__fes_result)"); err != nil {
L.GetGlobal("__fes_result")
if s := L.ToString(-1); s != "" {
return []byte(s), nil
}
val := L.Get(-1)
L.Pop(1)
if val == lua.LNil {
return []byte(""), nil
}
if s := L.ToString(-1); s != "" {
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("tostring"),
NRet: 1,
Protect: true,
}, val); err != nil {
return nil, err
}
s := L.ToString(-1)
L.Pop(1)
if s != "" {
return []byte(s), nil
}
L.GetGlobal("require")
L.Push(lua.LString("std"))
L.Call(1, 1)
ret := L.Get(-1)
L.Pop(1)
tbl, ok := ret.(*lua.LTable)
if !ok {
return nil, errors.New("Could not determine protocol")
}
proto := tbl.RawGetString("proto")
protoTbl, ok := proto.(*lua.LTable)
if !ok {
return nil, errors.New("Could not determine protocol")
}
protoTbl.ForEach(func(key lua.LValue, value lua.LValue) {
if str, ok := value.(lua.LString); ok {
switch str.String() {
case "http":
setBuffer.protos.http = true
case "gemini":
setBuffer.protos.gemini = true
}
}
})
return []byte(""), nil
}