Sharing fixes, live preview
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m19s

This commit is contained in:
Elijah 2026-05-22 19:19:15 -07:00
parent 27fd6fa4f5
commit a52f1d63f7
4 changed files with 350 additions and 92 deletions

View file

@ -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)
}