2020-04-27 16:46:23 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
2020-05-18 17:42:23 +00:00
|
|
|
"net/http"
|
2020-04-27 16:46:23 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
|
|
|
|
2020-05-08 14:37:43 +00:00
|
|
|
"github.com/ajnavarro/distribyted"
|
2020-04-27 16:46:23 +00:00
|
|
|
"github.com/ajnavarro/distribyted/config"
|
|
|
|
"github.com/ajnavarro/distribyted/mount"
|
2020-05-02 12:06:18 +00:00
|
|
|
"github.com/ajnavarro/distribyted/stats"
|
2020-05-13 17:02:51 +00:00
|
|
|
tlog "github.com/anacrolix/log"
|
2020-04-27 16:46:23 +00:00
|
|
|
"github.com/anacrolix/missinggo/v2/filecache"
|
|
|
|
"github.com/anacrolix/torrent"
|
|
|
|
"github.com/anacrolix/torrent/storage"
|
2020-05-08 14:37:43 +00:00
|
|
|
"github.com/gin-contrib/static"
|
2020-04-27 16:46:23 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/goccy/go-yaml"
|
2020-05-18 17:42:23 +00:00
|
|
|
"github.com/shurcooL/httpfs/html/vfstemplate"
|
2020-07-27 09:20:19 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
ginlogrus "github.com/toorop/gin-logrus"
|
2020-04-27 16:46:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2020-07-14 11:55:08 +00:00
|
|
|
|
2020-04-27 16:46:23 +00:00
|
|
|
var configPath string
|
|
|
|
if len(os.Args) < 2 {
|
|
|
|
configPath = "./config.yaml"
|
|
|
|
} else {
|
|
|
|
configPath = os.Args[1]
|
|
|
|
}
|
|
|
|
|
2020-07-27 09:20:19 +00:00
|
|
|
log := logrus.New()
|
|
|
|
log.SetFormatter(&logrus.TextFormatter{})
|
|
|
|
log.SetLevel(logrus.InfoLevel)
|
2020-07-14 11:55:08 +00:00
|
|
|
|
2020-04-27 16:46:23 +00:00
|
|
|
f, err := ioutil.ReadFile(configPath)
|
|
|
|
if err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error reading configuration file")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
conf := &config.Root{}
|
|
|
|
if err := yaml.Unmarshal(f, conf); err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error parsing configuration file")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
conf = config.AddDefaults(conf)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(conf.MetadataFolder, 0770); err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error creating metadata folder")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fc, err := filecache.NewCache(conf.MetadataFolder)
|
|
|
|
if err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error creating cache")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fc.SetCapacity(conf.MaxCacheSize * 1024 * 1024)
|
|
|
|
st := storage.NewResourcePieces(fc.AsResourceProvider())
|
|
|
|
|
|
|
|
// TODO download and upload limits
|
|
|
|
torrentCfg := torrent.NewDefaultClientConfig()
|
2020-05-13 17:02:51 +00:00
|
|
|
torrentCfg.Logger = tlog.Default.WithDefaultLevel(tlog.Info).FilterLevel(tlog.Info)
|
2020-04-27 16:46:23 +00:00
|
|
|
torrentCfg.Seed = true
|
|
|
|
torrentCfg.DisableTCP = true
|
|
|
|
torrentCfg.DefaultStorage = st
|
|
|
|
|
|
|
|
c, err := torrent.NewClient(torrentCfg)
|
|
|
|
if err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error initializing torrent client")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:18 +00:00
|
|
|
ss := stats.NewTorrent()
|
2020-06-03 09:15:01 +00:00
|
|
|
mountService := mount.NewHandler(c, ss)
|
2020-05-02 12:06:18 +00:00
|
|
|
|
2020-07-27 09:20:19 +00:00
|
|
|
defer func() {
|
|
|
|
tryClose(log, c, mountService)
|
|
|
|
}()
|
|
|
|
|
2020-04-27 16:46:23 +00:00
|
|
|
sigChan := make(chan os.Signal)
|
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-sigChan
|
2020-07-27 09:20:19 +00:00
|
|
|
tryClose(log, c, mountService)
|
2020-04-27 16:46:23 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
for _, mp := range conf.MountPoints {
|
|
|
|
if err := mountService.Mount(mp); err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).WithField("path", mp.Path).Error("error mounting folder")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:19:35 +00:00
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
|
2020-07-27 09:20:19 +00:00
|
|
|
r := gin.New()
|
|
|
|
|
|
|
|
r.Use(ginlogrus.Logger(log), gin.Recovery())
|
|
|
|
|
2020-07-14 11:55:08 +00:00
|
|
|
assets := distribyted.NewBinaryFileSystem(distribyted.HttpFS, "/assets")
|
2020-05-18 17:42:23 +00:00
|
|
|
r.Use(static.Serve("/assets", assets))
|
2020-07-14 11:55:08 +00:00
|
|
|
t, err := vfstemplate.ParseGlob(distribyted.HttpFS, nil, "/templates/*")
|
2020-05-08 14:37:43 +00:00
|
|
|
if err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error parsing html template")
|
2020-05-08 14:37:43 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 17:42:23 +00:00
|
|
|
r.SetHTMLTemplate(t)
|
|
|
|
|
|
|
|
r.GET("/", func(c *gin.Context) {
|
|
|
|
c.HTML(http.StatusOK, "index.html", nil)
|
|
|
|
})
|
|
|
|
|
|
|
|
r.GET("/routes", func(c *gin.Context) {
|
|
|
|
c.HTML(http.StatusOK, "routes.html", ss.RoutesStats())
|
|
|
|
})
|
2020-04-27 16:46:23 +00:00
|
|
|
|
2020-05-08 14:37:43 +00:00
|
|
|
r.GET("/api/status", func(ctx *gin.Context) {
|
2020-04-27 16:46:23 +00:00
|
|
|
ctx.JSON(200, gin.H{
|
|
|
|
"cacheItems": fc.Info().NumItems,
|
|
|
|
"cacheFilled": fc.Info().Filled / 1024 / 1024,
|
|
|
|
"cacheCapacity": fc.Info().Capacity / 1024 / 1024,
|
2020-05-18 17:42:23 +00:00
|
|
|
"torrentStats": ss.GlobalStats(),
|
2020-04-27 16:46:23 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-18 17:42:23 +00:00
|
|
|
r.GET("/api/routes", func(ctx *gin.Context) {
|
|
|
|
stats := ss.RoutesStats()
|
2020-05-08 14:37:43 +00:00
|
|
|
ctx.JSON(200, stats)
|
|
|
|
})
|
|
|
|
|
2020-07-14 11:55:08 +00:00
|
|
|
//TODO add port from configuration
|
2020-04-27 16:46:23 +00:00
|
|
|
if err := r.Run(":4444"); err != nil {
|
2020-07-27 09:20:19 +00:00
|
|
|
log.WithError(err).Error("error initializing server")
|
|
|
|
return
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-27 09:20:19 +00:00
|
|
|
|
|
|
|
func tryClose(log *logrus.Logger, c *torrent.Client, mountService *mount.Handler) {
|
|
|
|
log.Info("closing torrent client...")
|
|
|
|
c.Close()
|
|
|
|
log.Info("unmounting fuse filesystem...")
|
|
|
|
mountService.Close()
|
|
|
|
|
|
|
|
log.Info("exiting")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|