Tone 3 سال پیش
والد
کامیت
a5d33bd850
3فایلهای تغییر یافته به همراه238 افزوده شده و 10 حذف شده
  1. 159 0
      cmd/serve.go
  2. 34 4
      web/fs.go
  3. 45 6
      web/index.html

+ 159 - 0
cmd/serve.go

@@ -0,0 +1,159 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"html/template"
+	"log"
+	"net/http"
+
+	"github.com/spf13/cobra"
+	"github.com/t0n3/sakuin/web"
+)
+
+type fileItem struct {
+	Name  string
+	Size  string
+	Date  string
+	IsDir bool
+	Path  string
+}
+
+type templateVariables struct {
+	Path  []breadcrumb
+	Files []fileItem
+}
+
+type breadcrumb struct {
+	Name string
+	Path string
+}
+
+var dataDir string
+
+// serveCmd represents the serve command
+var serveCmd = &cobra.Command{
+	Use:   "serve",
+	Short: "Start the HTTP server",
+	Run: func(cmd *cobra.Command, args []string) {
+		log.Println("Starting Sakuin HTTP Server")
+		mux := http.NewServeMux()
+		mux.Handle("/assets/", web.AssetsHandler("/assets/", "dist"))
+		mux.HandleFunc("/", serve)
+		http.ListenAndServe(":3000", mux)
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(serveCmd)
+
+	// Here you will define your flags and configuration settings.
+
+	// Cobra supports Persistent Flags which will work for this command
+	// and all subcommands, e.g.:
+	// serveCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+	// Cobra supports local flags which will only run when this command
+	// is called directly, e.g.:
+	// serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
+
+func serve(w http.ResponseWriter, r *http.Request) {
+	templateVars := templateVariables{}
+
+	// Prepare the template
+	tmpl, err := template.ParseFS(web.Index, "index.html")
+	if err != nil {
+		// Log the detailed error
+		log.Println(err.Error())
+		// Return a generic "Internal Server Error" message
+		http.Error(w, http.StatusText(500), 500)
+		return
+	}
+
+	// Return file listing in the template
+	if err := tmpl.ExecuteTemplate(w, "index.html", templateVars); err != nil {
+		log.Println(err.Error())
+		http.Error(w, http.StatusText(500), 500)
+	}
+	log.Printf("200 - DIR %s\n", "/")
+
+	// // Filepath, from the root data dir
+	// fp := filepath.Join(dataDir, filepath.Clean(r.URL.Path))
+	// // Cleaned filepath, without the root data dir, used for template rendering purpose
+	// cfp := strings.Replace(fp, dataDir, "", 1)
+
+	// // Return a 404 if the template doesn't exist
+	// info, err := os.Stat(fp)
+	// if err != nil {
+	// 	if os.IsNotExist(err) {
+	// 		http.NotFound(w, r)
+	// 		log.Println(fmt.Sprintf("404 - %s", cfp))
+	// 		return
+	// 	}
+	// }
+
+	// Return a 404 if the request is for a directory
+	// if info.IsDir() {
+	// 	files, err := ioutil.ReadDir(fp)
+	// 	if err != nil {
+	// 		log.Fatal(err)
+	// 	}
+
+	// 	// Init template variables
+	// 	tplVars := templateVariables{}
+
+	// 	// Construct the breadcrumb
+	// 	path := strings.Split(cfp, "/")
+	// 	for len(path) > 0 {
+	// 		b := breadcrumb{
+	// 			Name: path[len(path)-1],
+	// 			Path: strings.Join(path, "/"),
+	// 		}
+	// 		path = path[:len(path)-1]
+	// 		tplVars.Path = append(tplVars.Path, b)
+	// 	}
+	// 	// Since the breadcrumb built is not very ordered...
+	// 	// REVERSE ALL THE THINGS
+	// 	for left, right := 0, len(tplVars.Path)-1; left < right; left, right = left+1, right-1 {
+	// 		tplVars.Path[left], tplVars.Path[right] = tplVars.Path[right], tplVars.Path[left]
+	// 	}
+
+	// 	// Establish list of files in the current directory
+	// 	for _, f := range files {
+	// 		tplVars.Files = append(tplVars.Files, fileItem{
+	// 			Name:  f.Name(),
+	// 			Size:  humanize.Bytes(uint64(f.Size())),
+	// 			Date:  humanize.Time(f.ModTime()),
+	// 			IsDir: f.IsDir(),
+	// 			Path:  filepath.Join(cfp, filepath.Clean(f.Name())),
+	// 		})
+	// 	}
+
+	// 	// Prepare the template
+	// 	tmpl, err := template.ParseFiles(lp)
+	// 	if err != nil {
+	// 		// Log the detailed error
+	// 		log.Println(err.Error())
+	// 		// Return a generic "Internal Server Error" message
+	// 		http.Error(w, http.StatusText(500), 500)
+	// 		return
+	// 	}
+
+	// 	// Return file listing in the template
+	// 	if err := tmpl.ExecuteTemplate(w, "layout", tplVars); err != nil {
+	// 		log.Println(err.Error())
+	// 		http.Error(w, http.StatusText(500), 500)
+	// 	}
+	// 	log.Println(fmt.Sprintf("200 - DIR %s", cfp))
+	// 	return
+	// }
+
+	// if !info.IsDir() {
+	// 	http.ServeFile(w, r, fp)
+	// 	log.Println(fmt.Sprintf("200 - FILE %s", cfp))
+	// 	return
+	// }
+}

+ 34 - 4
web/fs.go

@@ -4,13 +4,43 @@ import (
 	"embed"
 	"io/fs"
 	"net/http"
+	"os"
+	"path"
 )
 
 //go:embed dist
 var assets embed.FS
 
-func AssetsHandler() http.Handler {
-	fsys := fs.FS(assets)
-	assetsStatic, _ := fs.Sub(fsys, "dist")
-	return http.FileServer(http.FS(assetsStatic))
+//go:embed index.html
+var Index embed.FS
+
+// fsFunc is short-hand for constructing a http.FileSystem
+// implementation
+type fsFunc func(name string) (fs.File, error)
+
+func (f fsFunc) Open(name string) (fs.File, error) {
+	return f(name)
+}
+
+// AssetsHandler returns an http.Handler that will serve files from
+// the Assets embed.FS.  When locating a file, it will strip the given
+// prefix from the request and prepend the root to the filesystem
+// lookup: typical prefix might be /assets/, and root would be dist.
+func AssetsHandler(prefix, root string) http.Handler {
+	handler := fsFunc(func(name string) (fs.File, error) {
+		assetPath := path.Join(root, name)
+
+		// If we can't find the asset, return the default index.html
+		// content
+		f, err := assets.Open(assetPath)
+		if os.IsNotExist(err) {
+			return assets.Open("dist/404.html")
+		}
+
+		// Otherwise assume this is a legitimate request routed
+		// correctly
+		return f, err
+	})
+
+	return http.StripPrefix(prefix, http.FileServer(http.FS(handler)))
 }

+ 45 - 6
web/index.html

@@ -3,18 +3,57 @@
     <head>
         <meta charset="utf-8">
         <title>Sakuin (索引)</title>
-        <link rel="stylesheet" href="dist/css/theme.css">
+        <link rel="stylesheet" href="assets/css/theme.css">
     </head>
     <body>
 
+        <nav class="uk-navbar-container uk-margin" uk-navbar>
+            <div class="uk-navbar-center">
+                <a class="uk-navbar-item uk-logo" href="#">Sakuin (索引)</a>
+            </div>
+        </nav>
+
         <div class="uk-container">
-            <div class="uk-card uk-card-body uk-card-primary">
-                <h3 class="uk-card-title">Example headline</h3>
 
-                <button class="uk-button uk-button-default" uk-tooltip="title: Hello World">Hover</button>
-            </div>
+            <ul class="uk-breadcrumb">
+                <li><a href="/">Home</a></li>
+                <li><a href="/test">test</a></li>
+                <li><span>Current</span></li>
+            </ul>
+
+            <table class="uk-table uk-table-striped uk-table-hover uk-table-middle">
+                <thead>
+                    <tr>
+                        <th>Name</th>
+                        <th class="uk-table-shrink uk-text-nowrap">Last Modified</th>
+                        <th class="uk-table-shrink uk-text-nowrap">Size</th>
+                        <th class="uk-table-shrink uk-text-nowrap">Action</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        <td class="uk-text-truncate">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap"><a href="" class="uk-icon-button" uk-icon="icon: cloud-download"></a></td>
+                    </tr>
+                    <tr>
+                        <td class="uk-text-truncate">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap"><a href="" class="uk-icon-button" uk-icon="icon: cloud-download"></a></td>
+                    </tr>
+                    <tr>
+                        <td class="uk-text-truncate">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap">Table Data</td>
+                        <td class="uk-text-nowrap"><a href="" class="uk-icon-button" uk-icon="icon: cloud-download"></a></td>
+                    </tr>
+                </tbody>
+            </table>
+            
         </div>
 
-        <script src="dist/bundle.js"></script>
+        <script src="assets/bundle.js"></script>
     </body>
 </html>