tstor/cmd/tstor/main.go
2024-12-16 00:43:04 +03:00

216 lines
5.4 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"log/slog"
"net"
nethttp "net/http"
_ "net/http/pprof"
"os"
"os/signal"
"syscall"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/ytdlp"
"git.kmsign.ru/royalcat/tstor/pkg/ctxbilly"
wnfs "git.kmsign.ru/royalcat/tstor/pkg/go-nfs"
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
"git.kmsign.ru/royalcat/tstor/src/config"
"git.kmsign.ru/royalcat/tstor/src/daemons"
"git.kmsign.ru/royalcat/tstor/src/delivery"
"git.kmsign.ru/royalcat/tstor/src/telemetry"
"git.kmsign.ru/royalcat/tstor/src/vfs"
"github.com/go-git/go-billy/v5/osfs"
"github.com/urfave/cli/v2"
_ "git.kmsign.ru/royalcat/tstor/pkg/rlog"
"git.kmsign.ru/royalcat/tstor/src/export/fuse"
"git.kmsign.ru/royalcat/tstor/src/export/httpfs"
"git.kmsign.ru/royalcat/tstor/src/export/nfs"
"git.kmsign.ru/royalcat/tstor/src/export/webdav"
)
const (
configFlag = "config"
portFlag = "http-port"
webDAVPortFlag = "webdav-port"
)
func main() {
app := &cli.App{
Name: "tstor",
Usage: "Torrent client with on-demand file downloading as a filesystem.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: configFlag,
Value: "./config.yaml",
Usage: "YAML file containing tstor configuration.",
},
},
Action: func(c *cli.Context) error {
return run(c.String(configFlag))
},
HideHelpCommand: true,
}
if err := app.Run(os.Args); err != nil {
print("problem starting application: ", err.Error())
}
}
func run(configPath string) error {
conf, err := config.Load(configPath)
if err != nil {
return fmt.Errorf("error loading configuration: %w", err)
}
// dlog.Load(&conf.Log)
ctx := context.Background()
client, err := telemetry.Setup(ctx, conf.OtelHttp)
if err != nil {
return err
}
defer client.Shutdown(ctx)
log := rlog.Component("run")
// TODO make optional
// err = syscall.Setpriority(syscall.PRIO_PGRP, 0, 19)
// if err != nil {
// log.Error(ctx, "set priority failed", rlog.Error(err))
// }
if err := os.MkdirAll(conf.SourceDir, 0744); err != nil {
return fmt.Errorf("error creating data folder: %w", err)
}
sourceFs := osfs.New(conf.SourceDir, osfs.WithBoundOS())
// tsrv, err := torrent.NewDaemon(sourceFs, conf.Sources.TorrentClient)
// if err != nil {
// return fmt.Errorf("error creating service: %w", err)
// }
err = os.MkdirAll("./ytdlp", 0744)
if err != nil {
return err
}
ytdlpsrv, err := ytdlp.NewService("./ytdlp")
if err != nil {
return err
}
qtdaemon, err := qbittorrent.NewDaemon(conf.Sources.QBittorrent)
if err != nil {
return fmt.Errorf("error creating qbittorrent daemon: %w", err)
}
sfs := daemons.NewHostedFS(
vfs.NewCtxBillyFs("/", ctxbilly.WrapFileSystem(sourceFs)),
qtdaemon, ytdlpsrv,
)
sfs, err = vfs.WrapLogFS(sfs)
if err != nil {
return err
}
// go func() {
// log := log.WithComponent("background-scanner")
// err := vfs.Walk(ctx, sfs, "/", func(path string, info fs.FileInfo, err error) error {
// return nil
// })
// if err != nil {
// log.Error(ctx, "error walking filesystem", rlog.Error(err))
// }
// }()
if conf.Mounts.Fuse.Enabled {
mh := fuse.NewHandler(conf.Mounts.Fuse.AllowOther, conf.Mounts.Fuse.Path)
err := mh.Mount(sfs)
if err != nil {
return fmt.Errorf("mount fuse error: %w", err)
}
defer mh.Unmount()
}
if conf.Mounts.WebDAV.Enabled {
go func() {
if err := webdav.NewWebDAVServer(sfs, conf.Mounts.WebDAV.Port, conf.Mounts.WebDAV.User, conf.Mounts.WebDAV.Pass); err != nil {
log.Error(ctx, "error starting webDAV", rlog.Error(err))
}
log.Warn(ctx, "webDAV configuration not found!")
}()
}
if conf.Mounts.HttpFs.Enabled {
go func() {
httpfs := httpfs.NewHTTPFS(sfs)
addr := fmt.Sprintf("0.0.0.0:%d", conf.Mounts.HttpFs.Port)
err = nethttp.ListenAndServe(addr, nethttp.FileServer(httpfs))
if err != nil {
log.Error(ctx, "error starting HTTPFS", rlog.Error(err))
}
// r := gin.New()
// r.GET("*filepath", func(c *gin.Context) {
// path := c.Param("filepath")
// c.FileFromFS(path, httpfs)
// })
log.Info(ctx, "starting HTTPFS", slog.String("address", addr))
// if err := r.Run(fmt.Sprintf("0.0.0.0:%d", conf.Mounts.HttpFs.Port)); err != nil {
// log.Error().Err(err).Msg("error starting HTTPFS")
// }
}()
}
if conf.Mounts.NFS.Enabled {
go func() {
log := log.WithComponent("NFS")
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", conf.Mounts.NFS.Port))
if err != nil {
log.Error(ctx, "failed to start TCP listener", rlog.Error(err))
return
}
log.Info(ctx, "starting NFS server", slog.String("address", listener.Addr().String()))
handler, err := nfs.NewNFSv3Handler(sfs, conf.Mounts.NFS)
if err != nil {
log.Error(ctx, "failed to create NFS handler", rlog.Error(err))
return
}
err = wnfs.Serve(listener, handler)
if err != nil {
log.Error(ctx, "error serving nfs", rlog.Error(err))
return
}
}()
}
go func() {
err := webdav.NewDirServer(conf.SourceDir, 36912, conf.Mounts.WebDAV.User, conf.Mounts.WebDAV.Pass)
if err != nil {
log.Error(ctx, "error starting webDAV", rlog.Error(err))
}
}()
go func() {
err := delivery.Run(nil, qtdaemon, sfs, conf)
if err != nil {
log.Error(ctx, "error initializing HTTP server", rlog.Error(err))
}
}()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
return errors.Join(
// tsrv.Close(ctx),
qtdaemon.Close(ctx),
)
}