diff --git a/server/web/api/httpfs/httpfs.go b/server/web/api/httpfs/httpfs.go new file mode 100644 index 00000000..3aef1324 --- /dev/null +++ b/server/web/api/httpfs/httpfs.go @@ -0,0 +1,112 @@ +package httpfs + +import ( + "strings" + "net/http" + "net/url" + "strconv" + "server/log" + "github.com/gin-gonic/gin" + "server/torr" + "github.com/pkg/errors" + "github.com/anacrolix/torrent" + + "server/torr/state" + + sf "github.com/sa-/slicefunk" +) + +func listDir(path string, folders [] string, files []string) string { + result := ` + + + + Directory listing for ` + path + ` +

Directory listing for ` + path + `



` + return result +} + + + +func HandleHttpfs(c *gin.Context) { + path := c.Param("path") + log.TLogln("URL path", path) + path = strings.Trim(path, "/") + + if path == "" { + newArray := sf.Map(torr.ListTorrent(), func(item * torr.Torrent) string { return item.Title }) + c.Header("Content-Type", "text/html; charset=utf-8") + c.String(200, listDir(path, newArray, []string{})) + } else { + folders := strings.Split(path, "/") + trName := folders[0] + folderPath := strings.Join(folders[1:], "/") +// for _, item := range torr.ListTorrent() { +// log.TLogln("Torrents found", item.Title) +// } + requestedTorrents := sf.Filter(torr.ListTorrent(), func(item * torr.Torrent) bool { return item.Title == trName }) + if len(requestedTorrents) != 1 { + c.AbortWithError(http.StatusBadRequest, errors.New("Torrent not found" + trName + ": " + strconv.Itoa(len(requestedTorrents)))) + return + } + requestedTorrent := torr.GetTorrent(requestedTorrents[0].Hash().HexString()) + if requestedTorrent == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + if requestedTorrent.Stat == state.TorrentInDB { + requestedTorrent = torr.LoadTorrent(requestedTorrent) + if requestedTorrent == nil { + c.AbortWithError(http.StatusInternalServerError, errors.New("error get torrent info")) + return + } + } + + newArray := sf.Filter(requestedTorrent.Files(), func(item * torrent.File) bool { return strings.HasPrefix(item.Path(), folderPath) }) + +// for _, item := range newArray { +// log.TLogln("Filtered files", item.Path(), " sha1: ")// + item.FileInfo()) +// } +// + if len(newArray) == 1 && newArray[0].Path() == folderPath { + log.TLogln("Downloading file:", requestedTorrent.Title, ":", newArray[0].Path()) + var index = -1 + for i, item := range requestedTorrent.Files() { + if item.Path() == folderPath { + index = i + 1 + break + } + } + names := strings.Split(newArray[0].Path(), "/") +// torr.Preload(requestedTorrent, index) + c.Header("Content-Disposition", `attachment; filename="`+names[len(names) - 1]+`"`) + c.Header("Content-Type", "application/octet-stream") + requestedTorrent.Stream(index, c.Request, c.Writer) + } else { + c.Header("Content-Type", "text/html; charset=utf-8") + folders := []string{} + files := []string{} + + for _, item := range newArray { + p := strings.TrimPrefix(item.Path(), folderPath + "/") + if strings.Contains(p, "/") { + folders = append(folders, strings.Split(p, "/")[0]) + } else { + files = append(files, p) + } + } + folders = sf.Unique(folders) + c.String(200, listDir(path, folders, files)) + } + } + +} diff --git a/server/web/api/route.go b/server/web/api/route.go index 24620b7c..557d4efb 100644 --- a/server/web/api/route.go +++ b/server/web/api/route.go @@ -3,6 +3,8 @@ package api import ( config "server/settings" "server/web/auth" + "net/http" + "server/web/api/httpfs" "github.com/gin-gonic/gin" ) @@ -50,4 +52,7 @@ func SetupRoute(route gin.IRouter) { } authorized.GET("/ffp/:hash/:id", ffp) + + authorized.GET("/httpfs", func(c *gin.Context) { c.Redirect(http.StatusFound, "/httpfs/") }) + authorized.GET("/httpfs/*path", httpfs.HandleHttpfs) } diff --git a/web/src/components/App/Sidebar.jsx b/web/src/components/App/Sidebar.jsx index 4654c706..6ff081ae 100644 --- a/web/src/components/App/Sidebar.jsx +++ b/web/src/components/App/Sidebar.jsx @@ -2,7 +2,7 @@ import Divider from '@material-ui/core/Divider' import ListItem from '@material-ui/core/ListItem' import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemText from '@material-ui/core/ListItemText' -import { CreditCard as CreditCardIcon } from '@material-ui/icons' +import { CreditCard as CreditCardIcon, Http as HttpIcon } from '@material-ui/icons' import List from '@material-ui/core/List' import { useTranslation } from 'react-i18next' import AddDialogButton from 'components/Add' @@ -60,6 +60,14 @@ const Sidebar = ({ isDrawerOpen, setIsDonationDialogOpen, isOffline, isLoading, + window.open('httpfs', '_blank')}> + + + + + + +