Sharing fixes, live preview
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m19s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m19s
This commit is contained in:
parent
27fd6fa4f5
commit
a52f1d63f7
4 changed files with 350 additions and 92 deletions
|
|
@ -1,10 +1,13 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"mime"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.elijahkuntz.com/Elijah/drive/config"
|
||||
|
|
@ -194,27 +197,58 @@ func (h *ShareHandler) GetPublicShare(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
// Send file info
|
||||
fullPath := filepath.Join(h.Config.StorageDir, filepath.FromSlash(path))
|
||||
targetRelPath := filepath.Clean(filepath.FromSlash(path))
|
||||
if subPath := c.Query("path"); subPath != "" {
|
||||
cleanSub := filepath.Clean(filepath.FromSlash(subPath))
|
||||
if strings.HasPrefix(cleanSub, "..") || strings.HasPrefix(cleanSub, "/") || strings.HasPrefix(cleanSub, "\\") {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "invalid path"})
|
||||
}
|
||||
targetRelPath = filepath.Join(targetRelPath, cleanSub)
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(h.Config.StorageDir, targetRelPath)
|
||||
info, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "file no longer exists"})
|
||||
}
|
||||
|
||||
// We return a signed download token or just allow the immediate request.
|
||||
// For simplicity, we'll return a short-lived download token JWT.
|
||||
// Actually, easier: the frontend calls GET /api/public/download/:id?pwd=xxx
|
||||
|
||||
mimeType := mime.TypeByExtension(filepath.Ext(info.Name()))
|
||||
if mimeType == "" {
|
||||
mimeType = "application/octet-stream"
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"name": filepath.Base(path),
|
||||
"size": info.Size(),
|
||||
"is_dir": info.IsDir(),
|
||||
response := fiber.Map{
|
||||
"name": info.Name(), // Use info.Name() instead of base of original path
|
||||
"size": info.Size(),
|
||||
"is_dir": info.IsDir(),
|
||||
"mime_type": mimeType,
|
||||
})
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
var files []fiber.Map
|
||||
entries, err := os.ReadDir(fullPath)
|
||||
if err == nil {
|
||||
for _, entry := range entries {
|
||||
entryInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
eMime := mime.TypeByExtension(filepath.Ext(entry.Name()))
|
||||
if eMime == "" {
|
||||
eMime = "application/octet-stream"
|
||||
}
|
||||
files = append(files, fiber.Map{
|
||||
"name": entry.Name(),
|
||||
"size": entryInfo.Size(),
|
||||
"is_dir": entry.IsDir(),
|
||||
"mime_type": eMime,
|
||||
})
|
||||
}
|
||||
}
|
||||
response["files"] = files
|
||||
}
|
||||
|
||||
return c.JSON(response)
|
||||
}
|
||||
|
||||
// DownloadPublicShare serves the file.
|
||||
|
|
@ -252,11 +286,67 @@ func (h *ShareHandler) DownloadPublicShare(c *fiber.Ctx) error {
|
|||
}
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(h.Config.StorageDir, filepath.FromSlash(path))
|
||||
|
||||
targetRelPath := filepath.Clean(filepath.FromSlash(path))
|
||||
if subPath := c.Query("path"); subPath != "" {
|
||||
cleanSub := filepath.Clean(filepath.FromSlash(subPath))
|
||||
if strings.HasPrefix(cleanSub, "..") || strings.HasPrefix(cleanSub, "/") || strings.HasPrefix(cleanSub, "\\") {
|
||||
return c.Status(fiber.StatusForbidden).SendString("invalid path")
|
||||
}
|
||||
targetRelPath = filepath.Join(targetRelPath, cleanSub)
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(h.Config.StorageDir, targetRelPath)
|
||||
info, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).SendString("not found")
|
||||
}
|
||||
|
||||
// Increment download counter
|
||||
h.DB.Exec(`UPDATE share_links SET download_count = download_count + 1 WHERE token = ?`, id)
|
||||
h.DB.AddAuditLog("share_downloaded", fmt.Sprintf("Downloaded share %s", id), c.IP())
|
||||
h.DB.AddAuditLog("share_downloaded", fmt.Sprintf("Downloaded share %s path %s", id, targetRelPath), c.IP())
|
||||
|
||||
if info.IsDir() {
|
||||
c.Set("Content-Type", "application/zip")
|
||||
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", filepath.Base(fullPath)))
|
||||
|
||||
c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
|
||||
zipWriter := zip.NewWriter(w)
|
||||
defer zipWriter.Close()
|
||||
|
||||
filepath.Walk(fullPath, func(p string, i os.FileInfo, e error) error {
|
||||
if e != nil || p == fullPath {
|
||||
return e
|
||||
}
|
||||
|
||||
relPath, e := filepath.Rel(fullPath, p)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
if i.IsDir() {
|
||||
relPath += "/"
|
||||
_, e = zipWriter.Create(relPath)
|
||||
return e
|
||||
}
|
||||
|
||||
zipFile, e := zipWriter.Create(relPath)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
fsFile, e := os.Open(p)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer fsFile.Close()
|
||||
|
||||
// Write file to zip
|
||||
_, e = bufio.NewReader(fsFile).WriteTo(zipFile)
|
||||
return e
|
||||
})
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.SendFile(fullPath)
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue