Fix FUSE attachment sizes and item filenames
This commit is contained in:
+29
-1
@@ -137,6 +137,30 @@ func (c *DocspellClient) GetItem(ctx context.Context, id string) (*SearchItem, e
|
|||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *DocspellClient) AttachmentSize(ctx context.Context, id string) (int64, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodHead, c.url("/api/v1/sec/attachment/"+url.PathEscape(id)), nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if c.Token != "" {
|
||||||
|
req.Header.Set(c.AuthHeader, c.Token)
|
||||||
|
}
|
||||||
|
httpClient := c.HTTPClient
|
||||||
|
if httpClient == nil {
|
||||||
|
httpClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
return 0, fmt.Errorf("%s %s: %s: %s", req.Method, req.URL, resp.Status, strings.TrimSpace(string(body)))
|
||||||
|
}
|
||||||
|
return resp.ContentLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *DocspellClient) DownloadAttachment(ctx context.Context, id string) ([]byte, error) {
|
func (c *DocspellClient) DownloadAttachment(ctx context.Context, id string) ([]byte, error) {
|
||||||
return c.doBytes(ctx, http.MethodGet, "/api/v1/sec/attachment/"+url.PathEscape(id), nil)
|
return c.doBytes(ctx, http.MethodGet, "/api/v1/sec/attachment/"+url.PathEscape(id), nil)
|
||||||
}
|
}
|
||||||
@@ -179,7 +203,11 @@ func (c *DocspellClient) do(req *http.Request) ([]byte, error) {
|
|||||||
if c.Token != "" {
|
if c.Token != "" {
|
||||||
req.Header.Set(c.AuthHeader, c.Token)
|
req.Header.Set(c.AuthHeader, c.Token)
|
||||||
}
|
}
|
||||||
resp, err := c.HTTPClient.Do(req)
|
httpClient := c.HTTPClient
|
||||||
|
if httpClient == nil {
|
||||||
|
httpClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func runMountCommand(args []string, cfg config, password string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("search items: %w", err)
|
return fmt.Errorf("search items: %w", err)
|
||||||
}
|
}
|
||||||
root := buildMountTree(client, items)
|
root := buildMountTree(context.Background(), client, items)
|
||||||
|
|
||||||
conn, err := fuse.Mount(
|
conn, err := fuse.Mount(
|
||||||
fsFlags.Arg(0),
|
fsFlags.Arg(0),
|
||||||
@@ -71,9 +71,12 @@ func runMountCommand(args []string, cfg config, password string) error {
|
|||||||
return fs.Serve(conn, &docspellFS{root: root})
|
return fs.Serve(conn, &docspellFS{root: root})
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildMountTree(client *DocspellClient, items []SearchItem) *dirNode {
|
func buildMountTree(ctx context.Context, client *DocspellClient, items []SearchItem) *dirNode {
|
||||||
root := &dirNode{name: "", children: map[string]fs.Node{}}
|
root := &dirNode{name: "", children: map[string]fs.Node{}}
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
if full, err := client.GetItem(ctx, item.ID); err == nil {
|
||||||
|
item = *full
|
||||||
|
}
|
||||||
folderName := "No Folder"
|
folderName := "No Folder"
|
||||||
if item.Folder != nil && item.Folder.Name != "" {
|
if item.Folder != nil && item.Folder.Name != "" {
|
||||||
folderName = item.Folder.Name
|
folderName = item.Folder.Name
|
||||||
@@ -94,15 +97,22 @@ func buildMountTree(client *DocspellClient, items []SearchItem) *dirNode {
|
|||||||
base = item.ID
|
base = item.ID
|
||||||
}
|
}
|
||||||
for i, att := range item.Attachments {
|
for i, att := range item.Attachments {
|
||||||
name := sanitizeName(att.Name)
|
ext := filepath.Ext(att.Name)
|
||||||
if name == "" {
|
if ext == "" {
|
||||||
name = fmt.Sprintf("%s-%d.pdf", base, i+1)
|
ext = ".pdf"
|
||||||
}
|
}
|
||||||
|
name := base + ext
|
||||||
if len(item.Attachments) > 1 {
|
if len(item.Attachments) > 1 {
|
||||||
name = fmt.Sprintf("%s - %02d - %s", base, i+1, name)
|
name = fmt.Sprintf("%s - %02d%s", base, i+1, ext)
|
||||||
}
|
}
|
||||||
name = uniqueName(dir.children, name)
|
size := att.Size
|
||||||
dir.children[name] = &fileNode{name: name, attachmentID: att.ID, size: uint64(att.Size), knownSize: att.Size > 0, modTime: modTime, client: client}
|
if size <= 0 {
|
||||||
|
if headSize, err := client.AttachmentSize(ctx, att.ID); err == nil && headSize > 0 {
|
||||||
|
size = headSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name = uniqueName(dir.children, sanitizeName(name))
|
||||||
|
dir.children[name] = &fileNode{name: name, attachmentID: att.ID, size: uint64(size), knownSize: size > 0, modTime: modTime, client: client}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return root
|
return root
|
||||||
@@ -189,6 +199,11 @@ func (f *fileNode) Attr(ctx context.Context, a *fuse.Attr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fileNode) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) error {
|
||||||
|
resp.Flags |= fuse.OpenDirectIO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
func (f *fileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
||||||
data, err := f.load(ctx)
|
data, err := f.load(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+6
-6
@@ -18,7 +18,7 @@ func TestBuildMountTreeGroupsByFolderAndDate(t *testing.T) {
|
|||||||
Attachments: []ItemAttachment{{ID: "att-1", Name: "invoice.pdf", Size: 123}},
|
Attachments: []ItemAttachment{{ID: "att-1", Name: "invoice.pdf", Size: 123}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
root := buildMountTree(&DocspellClient{}, items)
|
root := buildMountTree(context.Background(), &DocspellClient{}, items)
|
||||||
folder, ok := root.children["Bills_Private"].(*dirNode)
|
folder, ok := root.children["Bills_Private"].(*dirNode)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected sanitized folder directory, got %#v", root.children)
|
t.Fatalf("expected sanitized folder directory, got %#v", root.children)
|
||||||
@@ -31,8 +31,8 @@ func TestBuildMountTreeGroupsByFolderAndDate(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected month directory, got %#v", year.children)
|
t.Fatalf("expected month directory, got %#v", year.children)
|
||||||
}
|
}
|
||||||
if _, ok := month.children["invoice.pdf"].(*fileNode); !ok {
|
if _, ok := month.children["Invoice_July.pdf"].(*fileNode); !ok {
|
||||||
t.Fatalf("expected attachment file, got %#v", month.children)
|
t.Fatalf("expected item-named attachment file, got %#v", month.children)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ func TestBuildMountTreeDeduplicatesAttachmentNames(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
root := buildMountTree(&DocspellClient{}, items)
|
root := buildMountTree(context.Background(), &DocspellClient{}, items)
|
||||||
folder := root.children["No Folder"].(*dirNode)
|
folder := root.children["No Folder"].(*dirNode)
|
||||||
date := folder.children["No Date"].(*dirNode)
|
date := folder.children["No Date"].(*dirNode)
|
||||||
if _, ok := date.children["Invoice - 01 - same.pdf"]; !ok {
|
if _, ok := date.children["Invoice - 01.pdf"]; !ok {
|
||||||
t.Fatalf("expected first unique name, got %#v", date.children)
|
t.Fatalf("expected first unique name, got %#v", date.children)
|
||||||
}
|
}
|
||||||
if _, ok := date.children["Invoice - 02 - same.pdf"]; !ok {
|
if _, ok := date.children["Invoice - 02.pdf"]; !ok {
|
||||||
t.Fatalf("expected second unique name, got %#v", date.children)
|
t.Fatalf("expected second unique name, got %#v", date.children)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user