diff --git a/main.go b/main.go index 30941bf..ce47324 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "embed" - "errors" "flag" "fmt" "os" @@ -103,16 +102,7 @@ func main() { runtime.ReadMemStats(&m) ui.Log("FRE memory usage when created %v Mb", m.TotalAlloc/1024/1024) - if err := server.Start(dir); err != nil { - if errors.Is(err, os.ErrNotExist) { - fmt.Fprintf(os.Stderr, "%s does not exist\n", dir) - fmt.Fprintf(os.Stderr, "Try: fes new %s\n", dir) - os.Exit(1) - } else { - fmt.Fprintln(os.Stderr, "Error:", err) - os.Exit(1) - } - } + server.Start(dir) default: fmt.Fprintf(os.Stderr, "Unknown command: %s\n", cmd) flag.Usage() diff --git a/modules/server/render.go b/modules/server/render.go new file mode 100644 index 0000000..1cf66d7 --- /dev/null +++ b/modules/server/render.go @@ -0,0 +1,6 @@ +package server + +/* returns a string of rendered html */ +func render(luapath string) (string, error) { + return "", nil +} diff --git a/modules/server/server.go b/modules/server/server.go index b253de5..3cf31e4 100644 --- a/modules/server/server.go +++ b/modules/server/server.go @@ -1,486 +1,18 @@ package server import ( - "errors" "fes/modules/config" - "fes/modules/ui" "fmt" - "html/template" - "io/fs" + "log" "net/http" - "os" - "path" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/pelletier/go-toml/v2" - 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 -} - -/* performs relavent handling based on the directory passaed - * - * Special directories - * - www/ <= contains lua routes. - * - static/ <= static content accessable at /static/path or /static/dir/path. - * - include/ <= globally accessable lua functions, cannot directly access "fes" right now. - * - archive/ <= contains user facing files such as archives or dists. - * - */ -func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base string, isStatic bool) error { - for _, entry := range entries { - path := filepath.Join(dir, entry.Name()) - if entry.IsDir() { - nextBase := joinBase(base, entry.Name()) - subEntries, err := os.ReadDir(path) - if err != nil { - return fmt.Errorf("failed to read directory %s: %w", path, err) - } - if err := handleDir(subEntries, path, routes, nextBase, isStatic); err != nil { - return err - } - continue - } - route := joinBase(base, entry.Name()) - if !isStatic && strings.HasSuffix(entry.Name(), ".lua") { - name := strings.TrimSuffix(entry.Name(), ".lua") - if name == "index" { - routes[basePath(base)] = path - routes[route] = path - continue - } - route = joinBase(base, name) - } else if !isStatic && strings.HasSuffix(entry.Name(), ".md") { - name := strings.TrimSuffix(entry.Name(), ".md") - if name == "index" { - routes[basePath(base)] = path - routes[route] = path - continue - } - route = joinBase(base, name) - } - routes[route] = path - } - return nil -} - -// TODO(vx-clutch): this should not be a function -func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable { - app := L.NewTable() - ents, err := os.ReadDir(includeDir) - if err != nil { - return app - } - for _, e := range ents { - if e.IsDir() || !strings.HasSuffix(e.Name(), ".lua") { - continue - } - base := strings.TrimSuffix(e.Name(), ".lua") - path := filepath.Join(includeDir, e.Name()) - if _, err := os.Stat(path); err != nil { - tbl := L.NewTable() - tbl.RawSetString("error", lua.LString(fmt.Sprintf("file not found: %s", path))) - app.RawSetString(base, tbl) - continue - } - if err := L.DoFile(path); err != nil { - tbl := L.NewTable() - tbl.RawSetString("error", lua.LString(err.Error())) - app.RawSetString(base, tbl) - 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 -} - -/* renders the given lua route */ -func renderRoute(entry string, cfg *config.AppConfig, requestData reqData) ([]byte, error) { - L := lua.NewState() - defer L.Close() - - libFiles, err := fs.ReadDir(config.Lib, "lib") - if err == nil { - for _, de := range libFiles { - if de.IsDir() || !strings.HasSuffix(de.Name(), ".lua") { - continue - } - path := filepath.Join("lib", de.Name()) - fileData, err := config.Lib.ReadFile(path) - if err != nil { - continue - } - L.DoString(string(fileData)) - } - } - - 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", loadIncludeModules(L, filepath.Join(".", "include"))) - - if cfg != nil { - site := L.NewTable() - site.RawSetString("version", lua.LString(cfg.App.Version)) - site.RawSetString("name", lua.LString(cfg.App.Name)) - authors := L.NewTable() - for i, a := range cfg.App.Authors { - authors.RawSetInt(i+1, lua.LString(a)) - } - site.RawSetString("authors", authors) - mod.RawSetString("site", site) - } - - 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(entry); 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 -} - -/* this indexes and generate the page for viewing the archive directory */ -func generateArchiveIndex(fsPath string, urlPath string) (string, error) { - info, err := os.Stat(fsPath) - if err != nil { - return "", err - } - if !info.IsDir() { - return "", fmt.Errorf("not a directory") - } - ents, err := os.ReadDir(fsPath) - if err != nil { - return "", err - } - type entryInfo struct { - name string - isDir bool - href string - size int64 - mod time.Time - } - var list []entryInfo - for _, e := range ents { - n := e.Name() - full := filepath.Join(fsPath, n) - st, err := os.Stat(full) - if err != nil { - continue - } - isd := st.IsDir() - displayName := n - if isd { - displayName = n + "/" - } - href := path.Join(urlPath, n) - if isd && !strings.HasSuffix(href, "/") { - href = href + "/" - } - size := int64(-1) - if !isd { - size = st.Size() - } - list = append(list, entryInfo{name: displayName, isDir: isd, href: href, size: size, mod: st.ModTime()}) - } - sort.Slice(list, func(i, j int) bool { - if list[i].isDir != list[j].isDir { - return list[i].isDir - } - return strings.ToLower(list[i].name) < strings.ToLower(list[j].name) - }) - - urlPath = basePath(strings.TrimPrefix(urlPath, "/archive")) - - var b strings.Builder - - b.WriteString("\n
")
-
- if urlPath != "/" {
- b.WriteString(
- `../` + "\n",
- )
- } else {
- b.WriteString(
- `../` + "\n",
- )
- }
-
- nameCol := 50
- for _, ei := range list {
- escapedName := template.HTMLEscapeString(ei.name)
- dateStr := ei.mod.Local().Format("02-Jan-2006 15:04")
- var sizeStr string
- if ei.isDir {
- sizeStr = "-"
- } else {
- sizeStr = fmt.Sprintf("%d", ei.size)
- }
- spaces := 1
- if len(escapedName) < nameCol {
- spaces = nameCol - len(escapedName)
- }
- line := `` + escapedName + `` + strings.Repeat(" ", spaces) + dateStr + strings.Repeat(" ", 19-len(sizeStr)) + sizeStr + "\n"
- b.WriteString(line)
- }
- b.WriteString("