mirror of
https://github.com/SinTan1729/privtracker.git
synced 2025-04-11 06:06:05 -05:00
193 lines
4.7 KiB
Go
193 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
"github.com/gofiber/fiber/v2/middleware/monitor"
|
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
|
"github.com/jackpal/bencode-go"
|
|
"golang.org/x/crypto/acme/autocert"
|
|
)
|
|
|
|
func main() {
|
|
go Cleanup()
|
|
config := fiber.Config{
|
|
ServerHeader: "privtracker",
|
|
ReadTimeout: time.Second * 125,
|
|
}
|
|
domains, tls := os.LookupEnv("DOMAINS")
|
|
if !tls {
|
|
config.EnableTrustedProxyCheck = true
|
|
config.TrustedProxies = []string{"127.0.0.1"}
|
|
config.ProxyHeader = fiber.HeaderXForwardedFor
|
|
}
|
|
app := fiber.New(config)
|
|
app.Use(recover.New())
|
|
app.Use(myLogger())
|
|
app.Get("/", docs)
|
|
app.Static("/", "docs", fiber.Static{MaxAge: 3600 * 24 * 7})
|
|
app.Get("/dashboard", monitor.New())
|
|
app.Get("/:room/announce", announce)
|
|
app.Get("/:room/scrape", scrape)
|
|
app.Server().LogAllErrors = true
|
|
if tls {
|
|
go redirect80(config)
|
|
split := strings.Split(domains, ",")
|
|
log.Fatal(app.Listener(newListener(split...)))
|
|
} else {
|
|
port := os.Getenv("PORT")
|
|
if port == "" {
|
|
port = "1337"
|
|
}
|
|
log.Fatal(app.Listen(":" + port))
|
|
}
|
|
}
|
|
|
|
func newListener(domains ...string) net.Listener {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cacheDir := filepath.Join(homeDir, ".cache", "golang-autocert")
|
|
m := &autocert.Manager{
|
|
Prompt: autocert.AcceptTOS,
|
|
HostPolicy: autocert.HostWhitelist(domains...),
|
|
Cache: autocert.DirCache(cacheDir),
|
|
}
|
|
cfg := &tls.Config{
|
|
GetCertificate: m.GetCertificate,
|
|
NextProtos: []string{
|
|
"http/1.1", "acme-tls/1",
|
|
},
|
|
}
|
|
ln, err := tls.Listen("tcp", ":443", cfg)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ln
|
|
}
|
|
|
|
func redirect80(config fiber.Config) {
|
|
config.DisableStartupMessage = true
|
|
app := fiber.New(config)
|
|
app.Use(func(c *fiber.Ctx) error {
|
|
return c.Redirect("https://privtracker.com/", fiber.StatusMovedPermanently)
|
|
})
|
|
log.Print(app.Listen(":80"))
|
|
}
|
|
|
|
func myLogger() fiber.Handler {
|
|
loggerConfig := logger.ConfigDefault
|
|
loggerConfig.Format = "${status} - ${latency} ${ip} ${method} ${path} ${bytesSent} - ${referer} - ${ua}\n"
|
|
return logger.New(loggerConfig)
|
|
}
|
|
|
|
func docs(c *fiber.Ctx) error {
|
|
if c.Hostname() != "privtracker.com" {
|
|
return c.Redirect("https://privtracker.com/", fiber.StatusMovedPermanently)
|
|
}
|
|
return c.Next()
|
|
}
|
|
|
|
type AnnounceRequest struct {
|
|
InfoHash string `query:"info_hash"`
|
|
PeerID string `query:"peer_id"`
|
|
IP string `query:"ip"`
|
|
Port uint16 `query:"port"`
|
|
Uploaded uint `query:"uploaded"`
|
|
Downloaded uint `query:"downloaded"`
|
|
Left uint `query:"left"`
|
|
Numwant uint `query:"numwant"`
|
|
Key string `query:"key"`
|
|
Compact bool `query:"compact"`
|
|
SupportCrypto bool `query:"supportcrypto"`
|
|
Event string `query:"event"`
|
|
}
|
|
|
|
func (req *AnnounceRequest) IsSeeding() bool {
|
|
return req.Left == 0
|
|
}
|
|
|
|
type AnnounceResponse struct {
|
|
Interval int `bencode:"interval"`
|
|
Complete int `bencode:"complete"`
|
|
Incomplete int `bencode:"incomplete"`
|
|
Peers []byte `bencode:"peers"`
|
|
PeersIPv6 []byte `bencode:"peers_ipv6"`
|
|
}
|
|
|
|
func announce(c *fiber.Ctx) error {
|
|
var req AnnounceRequest
|
|
err := c.QueryParser(&req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.IP = c.IP()
|
|
if req.Numwant == 0 {
|
|
req.Numwant = 30
|
|
}
|
|
switch req.Event {
|
|
case "stopped":
|
|
DeletePeer(c.Params("room"), req.InfoHash, req.IP, req.Port)
|
|
case "completed":
|
|
GraduateLeecher(c.Params("room"), req.InfoHash, req.IP, req.Port)
|
|
default:
|
|
PutPeer(c.Params("room"), req.InfoHash, req.IP, req.Port, req.IsSeeding())
|
|
}
|
|
peersIPv4, peersIPv6, numSeeders, numLeechers := GetPeers(c.Params("room"), req.InfoHash, req.IP, req.Port, req.IsSeeding(), req.Numwant)
|
|
interval := 120
|
|
if numSeeders == 0 {
|
|
interval /= 2
|
|
} else if numLeechers == 0 {
|
|
interval *= 2
|
|
}
|
|
resp := AnnounceResponse{
|
|
Interval: interval,
|
|
Complete: numSeeders,
|
|
Incomplete: numLeechers,
|
|
Peers: peersIPv4,
|
|
PeersIPv6: peersIPv6,
|
|
}
|
|
defer c.Response().SetConnectionClose()
|
|
return bencode.Marshal(c, resp)
|
|
}
|
|
|
|
type ScrapeRequest struct {
|
|
InfoHash string `query:"info_hash"`
|
|
}
|
|
|
|
type ScrapeResponse struct {
|
|
Files map[string]Stat `bencode:"files"`
|
|
}
|
|
|
|
type Stat struct {
|
|
Complete int `bencode:"complete"`
|
|
Incomplete int `bencode:"incomplete"`
|
|
// Downloaded uint `bencode:"downloaded"`
|
|
}
|
|
|
|
func scrape(c *fiber.Ctx) error {
|
|
var req ScrapeRequest
|
|
err := c.QueryParser(&req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
numSeeders, numLeechers := GetStats(c.Params("room"), req.InfoHash)
|
|
resp := ScrapeResponse{
|
|
Files: map[string]Stat{
|
|
req.InfoHash: {
|
|
Complete: numSeeders,
|
|
Incomplete: numLeechers,
|
|
},
|
|
},
|
|
}
|
|
return bencode.Marshal(c, resp)
|
|
}
|