diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml new file mode 100644 index 0000000..f50f4a4 --- /dev/null +++ b/.github/workflows/releases.yml @@ -0,0 +1,34 @@ +name: Releases + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build + run: chmod +x build.sh && ./build.sh + + - name: Package + run: | + mkdir dist && tar -czvf dist/sfs-linux-amd64.tar.gz sfs + mv sfs.exe dist/sfs-windows-amd64.exe + + - name: Upload Release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: dist/* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1458d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +sfs* +.idea/ diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..a562557 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +flags="-s -w" +export CGO_ENABLED=0 +go build -ldflags="$flags" +GOOS=windows GOARCH=amd64 go build -ldflags="$flags" diff --git a/main.go b/main.go index a7b7944..4732fd7 100644 --- a/main.go +++ b/main.go @@ -3,92 +3,97 @@ package main import ( "flag" "fmt" - "io/fs" "net/http" "os" "path" + "sort" "strconv" "strings" ) var ( - port *string - root *string + Port *string + Root *string ) -type InfoShow struct { - Name string - Time string - Size string - Path string -} - -type FileInfo struct { - Path string - Obj *os.File - Info fs.FileInfo -} - func main() { - port = flag.String("port", "8080", "server port") - root = flag.String("path", "./", "server root path") + Port = flag.String("port", "8080", "server port") + Root = flag.String("path", "./", "server root path") flag.Parse() - fmt.Println("start HTTP server @ 0.0.0.0:" + *port + "\n" + "load storage @ " + *root) + fmt.Printf("start HTTP server @ 0.0.0.0:%s\nload storage @ %s", *Port, *Root) - http.HandleFunc("/", FileServer) - http.ListenAndServe(":"+*port, nil) + http.HandleFunc("/", fileServer) + err := http.ListenAndServe(":"+*Port, nil) + if err != nil { + fmt.Printf("start error: %v", err) + os.Exit(1) + } } -func FileServer(w http.ResponseWriter, req *http.Request) { - var file FileInfo - var info InfoShow - - file.Path = path.Join(*root, req.URL.Path) - - var err error - file.Obj, err = os.Open(file.Path) +func fileServer(w http.ResponseWriter, req *http.Request) { + fPath := path.Join(*Root, req.URL.Path) + fObj, err := os.Open(fPath) if err != nil { http.NotFound(w, req) return } - defer file.Obj.Close() - - file.Info, _ = file.Obj.Stat() - - if file.Info.IsDir() { - fileLists, _ := file.Obj.Readdir(-1) - - w.Write([]byte("
../" + "\n")) - for _, f := range fileLists { - var list string - - info.Time = f.ModTime().Format("2006-01-02 15:04:05") - info.Name = f.Name() - info.Path = path.Join(req.URL.Path, info.Name) - - lenName := max(45-len(info.Name), 4) + defer fObj.Close() + fInfo, _ := fObj.Stat() + + if fInfo.IsDir() { + fList, _ := fObj.Readdir(-1) + sort.SliceStable(fList, func(i, j int) bool { + if fList[i].IsDir() == fList[j].IsDir() { + return fList[i].Name() < fList[j].Name() + } + return fList[i].IsDir() && !fList[j].IsDir() + }) + + var oLastIndex string + oIndex := req.URL.Path + if oIndex == "/" { + oLastIndex = oIndex + } else { + oLastIndex = path.Dir(oIndex) + oIndex += "/" + } - if f.IsDir() { - list = "" + info.Name + "/" + strings.Repeat(" ", lenName-1) + info.Time + head := fmt.Sprintf("Index of %s
../\n", oIndex, oLastIndex) + _, _ = w.Write([]byte(head)) + + for _, _f := range fList { + var li string + mName := _f.Name() + mUrl := path.Join(req.URL.Path, mName) + mTime := _f.ModTime().Format("2006-01-02 15:04:05") + mNameLength := max(50-len(mName), 1) + const mSizeLength = 19 + + if _f.IsDir() { + sn := strings.Repeat(" ", mNameLength) + sl := strings.Repeat(" ", mSizeLength) + li = fmt.Sprintf("%s/%s%s%s-\n", mUrl, mName, sn, mTime, sl) } else { - infoSize := f.Size() - if infoSize > 10240 { - infoSize >>= 10 - info.Size = strconv.FormatInt(infoSize, 10) + "kb" + var mSize string + _size := _f.Size() + + if _size > 10240 { + _size >>= 10 + mSize = strconv.FormatInt(_size, 10) + "kb" } else { - info.Size = strconv.FormatInt(infoSize, 10) + mSize = strconv.FormatInt(_size, 10) } - lenSize := max(15-len(info.Size), 4) - - list = "" + info.Name + "" + strings.Repeat(" ", lenName) + info.Time + strings.Repeat(" ", lenSize) + info.Size + sn := strings.Repeat(" ", mNameLength+1) + sl := strings.Repeat(" ", max(mSizeLength-len(mSize), 1)+1) + li = fmt.Sprintf("%s%s%s%s%s\n", mUrl, mName, sn, mTime, sl, mSize) } - w.Write([]byte(list + "\n")) + _, _ = w.Write([]byte(li)) } - w.Write([]byte("")) + _, _ = w.Write([]byte("