Skip to content

Commit 4781af1

Browse files
committed
feat(serverHandler): add sort support
1 parent 207b074 commit 4781af1

File tree

18 files changed

+732
-100
lines changed

18 files changed

+732
-100
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ server [options]
116116
Use virtual empty directory as root directory.
117117
Useful to share alias directories only.
118118
119+
--default-sort <sortBy>
120+
Default sort rule for files and directories.
121+
Available sort key:
122+
- `n` sort by name ascending
123+
- `N` sort by name descending
124+
- `s` sort by size ascending
125+
- `S` sort by size descending
126+
- `t` sort by modify time ascending
127+
- `T` sort by modify time descending
128+
- `_` no sort
129+
Directory sort:
130+
- `/<key>` directories before files
131+
- `<key>/` directories after files
132+
- `<key>` directories mixed with files
133+
119134
-I|--dir-index <file> ...
120135
Specify default index file for directory.
121136

README.zh-CN.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ server [选项]
114114
使用空的虚拟目录作为根目录。
115115
在仅需挂载别名的情况下较实用。
116116
117+
--default-sort <排序规则>
118+
指定文件和目录的默认排序规则。
119+
可用的排序key:
120+
- `n` 按名称递增排序
121+
- `N` 按名称递减排序
122+
- `s` 按大小递增排序
123+
- `S` 按大小递减排序
124+
- `t` 按修改时间递增排序
125+
- `T` 按修改时间递减排序
126+
- `_` 不排序
127+
目录顺序:
128+
- `/<key>` 目录在文件之前
129+
- `<key>/` 目录在文件之后
130+
- `<key>` 目录与文件混合
131+
117132
-I|--dir-index <文件> ...
118133
指定目录默认页面文件。
119134

doc/en-US/api.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
# Render page of specified path
22
```
3-
GET <path>
3+
GET <path>[?sort=<sortBy>]
44
```
55
Should work no matter tailing “/” is present or not in path.
66

7+
Available sort key:
8+
- `n` sort by name ascending
9+
- `N` sort by name descending
10+
- `s` sort by size ascending
11+
- `S` sort by size descending
12+
- `t` sort by modify time ascending
13+
- `T` sort by modify time descending
14+
- `_` no sort
15+
16+
Directory sort:
17+
- `/<key>` directories before files
18+
- `<key>/` directories after files
19+
- `<key>` directories mixed with files
20+
721
Example:
822
```sh
923
curl http://localhost/ghfs/
24+
curl http://localhost/ghfs/?sort=/T
1025
```
1126

1227
# Get JSON data of specified path
1328
```
14-
GET <path>?json
29+
GET <path>?json[&sort=key]
1530
```
1631

1732
Example:

doc/zh-CN/api.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
# 为指定路径显示页面
22
```
3-
GET <path>
3+
GET <path>[?sort=sortBy]
44
```
55
无论路径是否以“/”结尾,都应正常工作。
66

7+
可用的排序key:
8+
- `n` 按名称递增排序
9+
- `N` 按名称递减排序
10+
- `s` 按大小递增排序
11+
- `S` 按大小递减排序
12+
- `t` 按修改时间递增排序
13+
- `T` 按修改时间递减排序
14+
- `_` 不排序
15+
16+
目录顺序:
17+
- `/<key>` 目录在文件之前
18+
- `<key>/` 目录在文件之后
19+
- `<key>` 目录与文件混合
20+
721
举例:
822
```sh
923
curl http://localhost/ghfs/
24+
curl http://localhost/ghfs/?sort=/T
1025
```
1126

1227
# 获取指定路径JSON形式的数据
1328
```
14-
GET <path>?json
29+
GET <path>?json[&sort=key]
1530
```
1631

1732
举例:

src/param/cli.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var cliCmd *goNixArgParser.Command
1818
func init() {
1919
cliCmd = goNixArgParser.NewSimpleCommand(os.Args[0], "Simple command line based HTTP file server to share local file system")
2020
options := cliCmd.Options()
21+
var opt goNixArgParser.Option
2122

2223
// define option
2324
var err error
@@ -27,6 +28,11 @@ func init() {
2728
err = options.AddFlags("emptyroot", []string{"-R", "--empty-root"}, "GHFS_EMPTY_ROOT", "use virtual empty root directory")
2829
serverErrHandler.CheckFatal(err)
2930

31+
opt = goNixArgParser.NewFlagValueOption("defaultsort", "--default-sort", "GHFS_DEFAULT_SORT", "/n", "default sort for files and directories")
32+
opt.Description = "Available sort key:\n- `n` sort by name ascending\n- `N` sort by name descending\n- `s` sort by size ascending\n- `S` sort by size descending\n- `t` sort by modify time ascending\n- `T` sort by modify time descending\n- `_` no sort\nDirectory sort:\n- `/<key>` directories before files\n- `<key>/` directories after files\n- `<key>` directories mixed with files\n"
33+
err = options.Add(opt)
34+
serverErrHandler.CheckFatal(err)
35+
3036
err = options.AddFlagsValues("dirindexes", []string{"-I", "--dir-index"}, "GHFS_DIR_INDEX", nil, "default index page for directory")
3137
serverErrHandler.CheckFatal(err)
3238

@@ -225,6 +231,7 @@ func doParseCli() []*Param {
225231
// normalize option
226232
param.Root, _ = result.GetString("root")
227233
param.EmptyRoot = result.HasKey("emptyroot")
234+
param.DefaultSort, _ = result.GetString("defaultsort")
228235
param.GlobalUpload = result.HasKey("globalupload")
229236
param.GlobalMkdir = result.HasKey("globalmkdir")
230237
param.GlobalDelete = result.HasKey("globaldelete")

src/param/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ type Param struct {
1414
Root string
1515
EmptyRoot bool
1616

17-
DirIndexes []string
18-
Aliases map[string]string
17+
DefaultSort string
18+
DirIndexes []string
19+
Aliases map[string]string
1920

2021
GlobalUpload bool
2122
UploadUrls []string

src/serverHandler/json.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ type jsonItem struct {
1515
}
1616

1717
type jsonResponseData struct {
18-
IsRoot bool `json:"isRoot"`
19-
Path string `json:"path"`
20-
Paths []*pathEntry `json:"paths"`
21-
SubItemPrefix string `json:"subItemPrefix"`
22-
CanUpload bool `json:"canUpload"`
23-
CanMkdir bool `json:"canMkdir"`
24-
CanDelete bool `json:"canDelete"`
25-
CanArchive bool `json:"canArchive"`
26-
CanCors bool `json:"canCors"`
27-
NeedAuth bool `json:"needAuth"`
18+
IsRoot bool `json:"isRoot"`
19+
Path string `json:"path"`
20+
Paths []*pathEntry `json:"paths"`
21+
SubItemPrefix string `json:"subItemPrefix"`
22+
ContextQueryString string `json:"contextQueryString"`
23+
CanUpload bool `json:"canUpload"`
24+
CanMkdir bool `json:"canMkdir"`
25+
CanDelete bool `json:"canDelete"`
26+
CanArchive bool `json:"canArchive"`
27+
CanCors bool `json:"canCors"`
28+
NeedAuth bool `json:"needAuth"`
2829

2930
Item *jsonItem `json:"item"`
3031
SubItems []*jsonItem `json:"subItems"`
@@ -55,16 +56,17 @@ func getJsonData(data *responseData) *jsonResponseData {
5556
}
5657

5758
return &jsonResponseData{
58-
IsRoot: data.IsRoot,
59-
Path: data.Path,
60-
Paths: data.Paths,
61-
SubItemPrefix: data.SubItemPrefix,
62-
CanUpload: data.CanUpload,
63-
CanMkdir: data.CanMkdir,
64-
CanDelete: data.CanDelete,
65-
CanArchive: data.CanArchive,
66-
CanCors: data.CanCors,
67-
NeedAuth: data.NeedAuth,
59+
IsRoot: data.IsRoot,
60+
Path: data.Path,
61+
Paths: data.Paths,
62+
SubItemPrefix: data.SubItemPrefix,
63+
ContextQueryString: string(data.ContextQueryString),
64+
CanUpload: data.CanUpload,
65+
CanMkdir: data.CanMkdir,
66+
CanDelete: data.CanDelete,
67+
CanArchive: data.CanArchive,
68+
CanCors: data.CanCors,
69+
NeedAuth: data.NeedAuth,
6870

6971
Item: item,
7072
SubItems: subItems,

src/serverHandler/main.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import (
1212
)
1313

1414
type handler struct {
15-
root string
16-
emptyRoot bool
17-
urlPrefix string
15+
root string
16+
emptyRoot bool
17+
defaultSort string
18+
urlPrefix string
1819

1920
dirIndexes []string
2021
aliases aliases
@@ -142,9 +143,10 @@ func NewHandler(
142143
}
143144

144145
h := &handler{
145-
root: root,
146-
emptyRoot: emptyRoot,
147-
urlPrefix: urlPrefix,
146+
root: root,
147+
emptyRoot: emptyRoot,
148+
defaultSort: p.DefaultSort,
149+
urlPrefix: urlPrefix,
148150

149151
dirIndexes: p.DirIndexes,
150152
aliases: aliases,

src/serverHandler/page.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@ func updateSubItemsHtml(data *responseData) {
1616

1717
for i, info := range data.SubItems {
1818
name := info.Name()
19-
displayName := tplutil.FormatFilename(name)
2019

20+
var displayName template.HTML
2121
var typ template.HTML
2222
var url string
2323
var readableSize template.HTML
2424

2525
if info.IsDir() {
26+
displayName = tplutil.FormatFilename(name) + "/"
2627
typ = TypeDir
27-
url = data.SubItemPrefix + name + "/"
28-
displayName += "/"
28+
if len(data.ContextQueryString) > 0 {
29+
url = data.SubItemPrefix + name + "/" + string(data.ContextQueryString)
30+
} else {
31+
url = data.SubItemPrefix + name + "/"
32+
}
2933
} else {
34+
displayName = tplutil.FormatFilename(name)
3035
typ = TypeFile
3136
url = data.SubItemPrefix + name
3237
readableSize = tplutil.FormatSize(info.Size())

src/serverHandler/responseData.go

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,19 @@ type responseData struct {
3131
errors []error
3232
Status int
3333

34-
IsRoot bool
35-
Path string
36-
Paths []*pathEntry
37-
RootRelPath string
38-
File *os.File
39-
Item os.FileInfo
40-
ItemName string
41-
SubItems []os.FileInfo
42-
AliasSubItems []os.FileInfo
43-
SubItemsHtml []*itemHtml
44-
SubItemPrefix string
34+
IsRoot bool
35+
Path string
36+
Paths []*pathEntry
37+
RootRelPath string
38+
File *os.File
39+
Item os.FileInfo
40+
ItemName string
41+
SubItems []os.FileInfo
42+
AliasSubItems []os.FileInfo
43+
SubItemsHtml []*itemHtml
44+
SubItemPrefix string
45+
SortState SortState
46+
ContextQueryString template.HTML
4547

4648
CanUpload bool
4749
CanMkdir bool
@@ -280,6 +282,8 @@ func (h *handler) getResponseData(r *http.Request) *responseData {
280282
status := http.StatusOK
281283
isRoot := rawReqPath == "/"
282284

285+
rawQuery := r.URL.RawQuery
286+
283287
pathEntries := getPathEntries(rawReqPath, tailSlash)
284288
var rootRelPath string
285289
if len(pathEntries) > 0 {
@@ -328,14 +332,19 @@ func (h *handler) getResponseData(r *http.Request) *responseData {
328332
}
329333

330334
subItems = h.FilterItems(subItems)
331-
sortSubItems(subItems)
335+
rawSortBy, sortState := sortSubItems(subItems, rawQuery, h.defaultSort)
332336

333337
if h.emptyRoot && status == http.StatusOK && r.RequestURI != "/" {
334338
status = http.StatusNotFound
335339
}
336340

337341
subItemPrefix := getSubItemPrefix(rawReqPath, tailSlash)
338342

343+
var contextQueryString template.HTML
344+
if len(rawSortBy) > 0 {
345+
contextQueryString = "?sort=" + template.HTML(rawSortBy)
346+
}
347+
339348
canUpload := h.getCanUpload(item, rawReqPath, reqFsPath)
340349
canMkdir := h.getCanMkdir(item, rawReqPath, reqFsPath)
341350
canDelete := h.getCanDelete(item, rawReqPath, reqFsPath)
@@ -344,7 +353,6 @@ func (h *handler) getResponseData(r *http.Request) *responseData {
344353
canCors := h.getCanCors(rawReqPath, reqFsPath)
345354
needAuth := h.getNeedAuth(rawReqPath, reqFsPath)
346355

347-
rawQuery := r.URL.RawQuery
348356
isUpload := false
349357
isMkdir := false
350358
isDelete := false
@@ -369,17 +377,19 @@ func (h *handler) getResponseData(r *http.Request) *responseData {
369377
errors: errs,
370378
Status: status,
371379

372-
IsRoot: isRoot,
373-
Path: rawReqPath,
374-
Paths: pathEntries,
375-
RootRelPath: rootRelPath,
376-
File: file,
377-
Item: item,
378-
ItemName: itemName,
379-
SubItems: subItems,
380-
AliasSubItems: aliasSubItems,
381-
SubItemsHtml: nil,
382-
SubItemPrefix: subItemPrefix,
380+
IsRoot: isRoot,
381+
Path: rawReqPath,
382+
Paths: pathEntries,
383+
RootRelPath: rootRelPath,
384+
File: file,
385+
Item: item,
386+
ItemName: itemName,
387+
SubItems: subItems,
388+
AliasSubItems: aliasSubItems,
389+
SubItemsHtml: nil,
390+
SubItemPrefix: subItemPrefix,
391+
SortState: sortState,
392+
ContextQueryString: contextQueryString,
383393

384394
CanUpload: canUpload,
385395
CanMkdir: canMkdir,

0 commit comments

Comments
 (0)