package server import ( "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 } /* returns a string of rendered html */ func render(luapath string, requestData reqData) ([]byte, error) { L := lua.NewState() defer L.Close() if lib, err := fs.ReadDir(config.Lib, "lib"); err == nil { for _, de := range lib { if de.IsDir() || !strings.HasSuffix(de.Name(), ".lua") { continue } path := filepath.Join("lib", de.Name()) if data, err := config.Lib.ReadFile(path); err != nil { continue } else { L.DoString(string(data)) } } } preloadLuaModule := func(name, path string) { L.PreloadModule(name, func(L *lua.LState) int { fileData, err := config.Lib.ReadFile(path) if err != nil { panic(err) } if err := L.DoString(string(fileData)); err != nil { panic(err) } L.Push(L.Get(-1)) return 1 }) } preloadLuaModule("lib.std", "lib/std.lua") preloadLuaModule("lib.symbol", "lib/symbol.lua") 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) } } mod.RawSetString("app", func() *lua.LTable { app := L.NewTable() includeDir := "include" includes, err := os.ReadDir(includeDir) if err != nil { return app // load no includes } 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 }()) 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 { L.Push(lua.LString(markdownToHTML(L.ToString(1)))) return 1 })) L.Push(mod) return 1 }) if err := L.DoFile(luapath); err != nil { return []byte(""), err } if L.GetTop() == 0 { 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 } return []byte(""), nil } if s := L.ToString(-1); s != "" { return []byte(s), nil } return []byte(""), nil }