// Simpliest server package main //go:generate genversion -out version.go -package main //go:generate folder2go -handler assets binAssets import ( "errors" "fmt" "io/ioutil" "net" "net/http" "os" "path/filepath" "strings" "text/template" "dev.hexasoftware.com/hxs/httpServe/binAssets" "dev.hexasoftware.com/stdio/wsrpc" "github.com/fsnotify/fsnotify" "github.com/gohxs/prettylog" "github.com/gohxs/webu" "github.com/gohxs/webu/chain" ) var ( log = prettylog.New("httpServe") tmpl = template.New("") ) func main() { prettylog.Global() log.Println("V:", Version) mux := http.NewServeMux() c := chain.New(webu.ChainLogger(prettylog.New("serve"))) mux.HandleFunc("/.httpServe/_reload/", wsrpc.New(wsrpcClient).ServeHTTP) mux.HandleFunc("/.httpServe/", binAssets.AssetHandleFunc) // Only logs this mux.HandleFunc("/", c.Build(fileServe)) // Load templates from binAssets tmplFiles := []string{ "tmpl/MD.tmpl", "tmpl/Folder.tmpl", } for _, v := range tmplFiles { _, err := tmpl.New(v).Parse(string(binAssets.Data[v])) if err != nil { log.Fatal("Internal error, loading templates") } } var port = 8080 for { addr := fmt.Sprintf(":%d", port) listener, err := net.Listen("tcp", addr) if err != nil { log.Println("Err opening", port, err) port++ log.Println("Trying port", port) continue } log.Println("Listening with port:", port) http.Serve(listener, mux) } //http.ListenAndServe(":8080", http.FileServer(http.Dir('.'))) } func wsrpcClient(ctx *wsrpc.ClientCtx) { log.Println("Ws connected") watcher, err := fsnotify.NewWatcher() // watcher per socket if err != nil { return } defer watcher.Close() ctx.Define("watch", func(params ...interface{}) (interface{}, error) { toWatch, ok := params[0].(string) if !ok { return nil, errors.New("Param invalid") } absFile, err := filepath.Abs(toWatch[1:]) if err != nil { return nil, err } err = watcher.Add(absFile) // remove root '/' prefix if err != nil { log.Println("Error watching", err) } // Request to watch something? return true, nil }) for { select { case event := <-watcher.Events: if event.Op&fsnotify.Remove != 0 { ctx.Call("reload") } case <-ctx.Done(): return } } } func handleMarkDown(w http.ResponseWriter, r *http.Request, path string) error { fileData, err := ioutil.ReadFile(path) if err != nil { return err } err = tmpl.ExecuteTemplate(w, "tmpl/MD.tmpl", map[string]interface{}{ "path": path, "content": string(fileData), }) return err } func handleFolder(w http.ResponseWriter, r *http.Request, path string) error { res, err := ioutil.ReadDir(path) if err != nil { return err } err = tmpl.ExecuteTemplate(w, "tmpl/Folder.tmpl", map[string]interface{}{ "path": path, "content": res, }) return err } func fileServe(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[1:] if path == "" { path = "index.html" if _, err := os.Stat(path); os.IsNotExist(err) { path = "." } } if strings.Contains(path, "..") { http.ServeFile(w, r, path) } //log.Printf("%s - %s", r.Method, r.URL.Path) fstat, err := os.Stat(path) if err != nil { webu.WriteStatus(w, http.StatusNotFound) return } if fstat.IsDir() { err := handleFolder(w, r, path) if err != nil { webu.WriteStatus(w, http.StatusInternalServerError, err) } return } if filepath.Ext(path) == ".md" { err := handleMarkDown(w, r, path) if err != nil { webu.WriteStatus(w, http.StatusInternalServerError, err) } return } http.ServeFile(w, r, path) }