From f3f852fefd70a352acf4407fe4c9cbf556fb1997 Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Mon, 10 Nov 2025 15:04:19 -0500 Subject: [PATCH] Use custom configuration, reducing dependencies --- .vscode/settings.json | 3 - cmd/root.go | 114 --------------------------- cmd/ytdl-web/main.go | 98 +++++++++++++++++++++++ config.example.yaml | 47 ----------- config/config.go | 129 ------------------------------- devenv.nix | 9 ++- go.mod | 25 ------ go.sum | 120 +--------------------------- main.go | 16 ---- nix/devenv.nix | 107 ------------------------- nix/package.nix | 9 --- pkg/config/var.go | 104 +++++++++++++++++++++++++ pkg/{server => routes}/routes.go | 6 +- pkg/server/options.go | 37 --------- pkg/server/server.go | 35 --------- pkg/serverctx/context.go | 28 +++++-- pkg/views/downloads.templ | 6 +- pkg/views/downloads_templ.go | 12 +-- pkg/views/helpers.go | 7 +- pkg/views/home.templ | 4 +- pkg/views/home_templ.go | 8 +- pkg/ytdl/cache/cache.go | 15 ++-- pkg/ytdl/config.go | 24 ++++++ pkg/ytdl/ytdl.go | 27 ++++--- 24 files changed, 300 insertions(+), 690 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 cmd/root.go create mode 100644 cmd/ytdl-web/main.go delete mode 100644 config.example.yaml delete mode 100644 config/config.go delete mode 100644 main.go delete mode 100644 nix/devenv.nix create mode 100644 pkg/config/var.go rename pkg/{server => routes}/routes.go (97%) delete mode 100644 pkg/server/options.go delete mode 100644 pkg/server/server.go create mode 100644 pkg/ytdl/config.go diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 9d14cfb..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ansible.python.interpreterPath": "/bin/python" -} \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index 55db51b..0000000 --- a/cmd/root.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright © 2024 Evan Fiordeliso -*/ -package cmd - -import ( - "errors" - "fmt" - "log/slog" - "os" - - "github.com/dgraph-io/badger/v2" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "go.fifitido.net/ytdl-web/config" - "go.fifitido.net/ytdl-web/pkg/server" - "go.fifitido.net/ytdl-web/pkg/utils" - "go.fifitido.net/ytdl-web/pkg/ytdl" - "go.fifitido.net/ytdl-web/pkg/ytdl/cache" -) - -var ( - cfgFile string - cfg *config.Config - - rootCmd = &cobra.Command{ - Use: "ytdl-web", - Short: "A web frontend for yt-dlp", - Long: `YTDL Web is a web application that grabs the links to videos -from over a thousand websites using the yt-dlp project under the hood.`, - RunE: func(cmd *cobra.Command, args []string) error { - initLogging() - logger := slog.Default() - - db, err := badger.Open( - badger. - DefaultOptions(cfg.Cache.DirPath). - WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))), - ) - if err != nil { - return err - } - defer db.Close() - - cache := cache.NewDefaultMetadataCache(db) - yt := ytdl.NewYtdl(cfg, slog.Default(), cache) - ytdl.SetDefault(yt) - - return server.ListenAndServe( - server.WithListenAddr(viper.GetString("http.listen")), - server.WithListenPort(viper.GetInt("http.port")), - server.WithLogger(logger.With("module", "server")), - ) - }, - } -) - -func Execute() error { - return rootCmd.Execute() -} - -func init() { - cobra.OnInitialize(initConfig) - - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/ytdl-web/config.yml)") - - rootCmd.PersistentFlags().IntP("port", "p", 8080, "port to listen on") - rootCmd.PersistentFlags().StringP("listen", "l", "", "address to listen on") - rootCmd.PersistentFlags().StringP("base-path", "b", "", "the base path, used when behind reverse proxy") - rootCmd.PersistentFlags().StringP("ytdlp-path", "y", "", "the path to the yt-dlp binary, used when it is not in $PATH") - rootCmd.PersistentFlags().BoolP("cookies-enabled", "C", false, "whether cookies are enabled") - rootCmd.PersistentFlags().StringP("cookies", "c", "", "the path to the cookies file") - - viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("port")) - viper.BindPFlag("http.listen", rootCmd.PersistentFlags().Lookup("listen")) - viper.BindPFlag("http.basePath", rootCmd.PersistentFlags().Lookup("base-path")) - viper.BindPFlag("ytdlp.binaryPath", rootCmd.PersistentFlags().Lookup("ytdlp-path")) - viper.BindPFlag("cookies.enabled", rootCmd.PersistentFlags().Lookup("cookies-enabled")) - viper.BindPFlag("cookies.filePath", rootCmd.PersistentFlags().Lookup("cookies")) -} - -func initConfig() { - var err error - if cfgFile != "" { - cfg, err = config.LoadConfig(cfgFile) - } else { - cfg, err = config.LoadConfig() - } - - notFound := &viper.ConfigFileNotFoundError{} - switch { - case err != nil && !errors.As(err, notFound): - cobra.CheckErr(err) - case err != nil && errors.As(err, notFound): - // The config file is optional, we shouldn't exit when the config is not found - break - default: - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) - } -} - -func initLogging() { - if cfg.IsProduction() { - slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ - AddSource: true, - Level: slog.LevelInfo, - }))) - } else { - slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ - AddSource: true, - Level: slog.LevelDebug, - }))) - } -} diff --git a/cmd/ytdl-web/main.go b/cmd/ytdl-web/main.go new file mode 100644 index 0000000..f4e5fe8 --- /dev/null +++ b/cmd/ytdl-web/main.go @@ -0,0 +1,98 @@ +/* +Copyright © 2024 Evan Fiordeliso +*/ +package main + +import ( + "flag" + "log/slog" + "net/http" + "os" + "time" + + "github.com/dgraph-io/badger/v2" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + slogchi "github.com/samber/slog-chi" + "go.fifitido.net/ytdl-web/pkg/config" + "go.fifitido.net/ytdl-web/pkg/routes" + "go.fifitido.net/ytdl-web/pkg/serverctx" + "go.fifitido.net/ytdl-web/pkg/utils" + "go.fifitido.net/ytdl-web/pkg/ytdl" + "go.fifitido.net/ytdl-web/pkg/ytdl/cache" +) + +var ( + env = config.String("env", "production", "the environment to run") + listenAddr = config.String("listen", ":8080", "address to listen on") + basePath = config.String("base_path", "/", "the base path, used when behind reverse proxy") + ytdlpPath = config.String("ytdlp.binary", "", "path to yt-dlp executable") + cacheTTL = config.Duration("cache.ttl", time.Hour, "the TTL for the cache") + cacheDir = config.String("cache.dir", "", "the directory to store the cache") + cookies = config.Bool("cookies", false, "whether cookies are enabled") + cookiesPath = config.String("cookies.path", "", "the path to the cookies file") + cookiesBrowser = config.String("cookies.browser", "", "the browser to use for cookies") + cookiesBrowserKeyring = config.String("cookies.browser.keyring", "", "the keyring to use for cookies") + cookiesBrowserProfile = config.String("cookies.browser.profile", "", "the profile to use for cookies") + cookiesBrowserContainer = config.String("cookies.browser.container", "", "the container to use for cookies") +) + +func main() { + flag.Parse() + + logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelInfo, + })) + + if env() == "development" { + logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + })) + } + + slog.SetDefault(logger) + + db, err := badger.Open( + badger. + DefaultOptions(cacheDir()). + WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))), + ) + if err != nil { + os.Exit(1) + } + defer db.Close() + + cache := cache.NewDefaultMetadataCache(db, cacheTTL()) + ytdl.SetDefault( + ytdl.NewYtdl(&ytdl.Config{ + BinaryPath: ytdlpPath(), + CookiesEnabled: cookies(), + CookiesFilePath: cookiesPath(), + CookiesBrowser: cookiesBrowser(), + CookiesBrowserKeyring: cookiesBrowserKeyring(), + CookiesBrowserProfile: cookiesBrowserProfile(), + CookiesBrowserContainer: cookiesBrowserContainer(), + }, logger, cache), + ) + + r := chi.NewRouter() + + r.Use( + serverctx.Middleware(basePath()), + middleware.RequestID, + middleware.RealIP, + slogchi.New(logger), + middleware.Recoverer, + ) + + r.Mount(basePath(), routes.Router()) + + logger.Info("Starting HTTP server", slog.String("listen", listenAddr())) + + if err := http.ListenAndServe(listenAddr(), r); err != nil { + logger.Error("failed to serve website", slog.Any("error", err)) + os.Exit(1) + } +} diff --git a/config.example.yaml b/config.example.yaml deleted file mode 100644 index fb9ff06..0000000 --- a/config.example.yaml +++ /dev/null @@ -1,47 +0,0 @@ ---- -# The server environment -# For dev environments use Development -# For prod environments use Production -# For staging envronments use Staging -env: Production -ytdlp: - # The path to the yt-dlp binary - # If it is already in your $PATH just yt-dlp will work. - binaryPath: yt-dlp -http: - # The port to listen on - port: 8080 - # The address to listen on - # For local only access use 127.0.0.1 - # For public access use 0.0.0.0 - listen: 0.0.0.0 - # The base path of the application, useful for reverse proxies - basePath: "" - - # A list of proxy servers to trust for security purposes - # Only needed when accessing app behind a proxy - trustedProxies: [] -cookies: - # Whether to use cookies when fetching the video metadata - enabled: false - - # The path to the netscape formatted cookies file - # See: https://www.reddit.com/r/youtubedl/wiki/cookies/ for details. - filePath: ~/.cookies - - # Settings for using cookies from a browser's cookies store - fromBrowser: - # The name of the browser to load cookies from. - # Currently supported browsers are: brave, chrome, chromium, edge, - # firefox, opera, safari, vivaldi. - browser: firefox - - # The keyring used for decrypting Chromium cookies on Linux - # Currently supported keyrings are: basictext, gnomekeyring, kwallet - keyring: basictext - - # The profile to load cookies from (Firefox) - profile: default - - # The container to load cookies from (Firefox) - container: none diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 5c4385a..0000000 --- a/config/config.go +++ /dev/null @@ -1,129 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path" - "strings" - "time" - - "github.com/adrg/xdg" - "github.com/spf13/viper" -) - -type Config struct { - Env string `mapstructure:"env"` - Ytdlp ConfigYtdlp `mapstructure:"ytdlp"` - HTTP ConfigHTTP `mapstructure:"http"` - Cache ConfigCache `mapstructure:"cache"` - Cookies ConfigCookies `mapstructure:"cookies"` -} - -func (c *Config) IsProduction() bool { - return c.Env == "Production" -} - -func (c *Config) IsDevelopment() bool { - return c.Env == "Development" -} - -func (c *Config) IsStaging() bool { - return c.Env == "Staging" -} - -type ConfigYtdlp struct { - BinaryPath string `mapstructure:"binaryPath"` -} - -type ConfigHTTP struct { - Port int `mapstructure:"port"` - Listen string `mapstructure:"listen"` - BasePath string `mapstructure:"basePath"` - TrustedProxies []string `mapstructure:"trustedProxies"` -} - -type ConfigCache struct { - TTL time.Duration `mapstructure:"ttl"` - DirPath string `mapstructure:"dirPath"` -} - -type ConfigCookies struct { - Enabled bool `mapstructure:"enabled"` - FilePath string `mapstructure:"filePath"` - FromBrowser ConfigCookiesFromBrowser `mapstructure:"fromBrowser"` -} - -type ConfigCookiesFromBrowser struct { - Browser string `mapstructure:"browser"` - Keyring string `mapstructure:"keyring"` - Profile string `mapstructure:"profile"` - Container string `mapstructure:"container"` -} - -func DefaultConfig() *Config { - return &Config{ - Env: "Production", - Ytdlp: ConfigYtdlp{BinaryPath: "yt-dlp"}, - HTTP: ConfigHTTP{ - Port: 8080, - Listen: "127.0.0.1", - BasePath: "/", - }, - Cache: ConfigCache{ - TTL: time.Hour, - DirPath: "/tmp/ytdl-web", - }, - Cookies: ConfigCookies{ - Enabled: false, - FilePath: "./cookies.txt", - FromBrowser: ConfigCookiesFromBrowser{}, - }, - } -} - -func LoadConfig(paths ...string) (*Config, error) { - viper.SetEnvPrefix("YTDL") - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.AutomaticEnv() - - viper.SetConfigName("config") - viper.SetConfigType("yaml") - - if len(paths) > 0 { - for _, p := range paths { - viper.AddConfigPath(path.Dir(p)) - } - } else { - envDir := os.Getenv("YTDL_CONFIGDIR") - if envDir != "" { - viper.AddConfigPath(envDir) - } - - viper.AddConfigPath(".") - - homeDir, err := os.UserHomeDir() - if err == nil { - viper.AddConfigPath(homeDir + "/.config/ytdl-web") - } - - viper.AddConfigPath(xdg.ConfigHome + "/ytdl-web") - for _, dir := range xdg.ConfigDirs { - viper.AddConfigPath(dir + "/ytdl-web") - } - } - - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - return nil, err - } - } - - config := DefaultConfig() - if err := viper.Unmarshal(config); err != nil { - return nil, err - } - - fmt.Printf("%#v\n", config) - - return config, nil -} diff --git a/devenv.nix b/devenv.nix index c030849..780c365 100644 --- a/devenv.nix +++ b/devenv.nix @@ -11,7 +11,12 @@ DOCKER_ORG = "apps"; DOCKER_PLATFORMS = "linux/amd64,linux/arm64"; - YTDL_ENV = "Development"; + YTDL_WEB_ENV = "development"; + YTDL_WEB_YTDLP_BINARY = "/usr/bin/yt-dlp"; + YTDL_WEB_LISTEN = ":8989"; + YTDL_WEB_CACHE_DIR = config.devenv.runtime + "/cache"; + YTDL_WEB_COOKIES = "true"; + YTDL_WEB_COOKIES_BROWSER = "firefox"; }; # https://devenv.sh/packages/ @@ -42,7 +47,7 @@ -X $VERSION_PKG.Build=$(build-id) -X $VERSION_PKG.BuildDate=$(build-date) -X $VERSION_PKG.BuiltBy=manual - " -o $BINARY_OUT . + " -o $BINARY_OUT ./cmd/ytdl-web ''; clean.exec = '' rm -rf ./dist/ ./out/ ./tmp/ diff --git a/go.mod b/go.mod index ac0f230..07b513b 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,9 @@ go 1.25.2 require ( github.com/a-h/templ v0.3.960 - github.com/adrg/xdg v0.5.3 github.com/dgraph-io/badger/v2 v2.2007.4 github.com/go-chi/chi/v5 v5.2.3 github.com/samber/slog-chi v1.17.0 - github.com/spf13/cobra v1.10.1 - github.com/spf13/viper v1.21.0 ) require ( @@ -18,35 +15,13 @@ require ( github.com/dgraph-io/ristretto v0.2.0 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/magiconair/properties v1.8.10 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sagikazarmark/locafero v0.12.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/afero v1.15.0 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/subosito/gotenv v1.6.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.46.0 // indirect golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.30.0 // indirect google.golang.org/protobuf v1.36.10 // indirect - gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 0b66dad..9b0cfb2 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,6 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM= github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= -github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= -github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= -github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= -github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -16,198 +12,86 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE= github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= -github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= -github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/samber/slog-chi v1.17.0 h1:zP66fV4LGF1y1Dg/+uHNY9Uxkmw1YNWaZGws6M6mPCk= github.com/samber/slog-chi v1.17.0/go.mod h1:a1iIuofF2gS1ii8aXIQhC6TEguLOhOvSM958fY5hToU= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go deleted file mode 100644 index 2b2336b..0000000 --- a/main.go +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright © 2024 Evan Fiordeliso -*/ -package main - -import ( - "os" - - "go.fifitido.net/ytdl-web/cmd" -) - -func main() { - if err := cmd.Execute(); err != nil { - os.Exit(1) - } -} diff --git a/nix/devenv.nix b/nix/devenv.nix deleted file mode 100644 index 1ce61dc..0000000 --- a/nix/devenv.nix +++ /dev/null @@ -1,107 +0,0 @@ -{ pkgs, ... }: - -{ - # https://devenv.sh/basics/ - env.NAME = "ytdl-web"; - env.BINARY_OUT = "./out/ytdl-web"; - env.VERSION = "v1.2.3"; - env.VERSION_PKG = "go.fifitido.net/ytdl-web/version"; - env.DOCKER_REGISTRY = "git.fifitido.net"; - env.DOCKER_ORG = "apps"; - env.DOCKER_PLATFORMS = "linux/amd64,linux/arm64"; - - env.YTDL_ENV = "Development"; - - # https://devenv.sh/packages/ - packages = with pkgs; [ - git - air - goreleaser - docker - docker-compose - buildkit - docker-buildx - yt-dlp - cobra-cli - ]; - - # https://devenv.sh/scripts/ - scripts.deps.exec = "go mod download"; - scripts.tidy.exec = "go mod tidy"; - scripts.check.exec = "goreleaser check"; - - scripts.build-id.exec = "git rev-parse --short HEAD"; - scripts.build-date.exec = "date -Iseconds"; - - scripts.build.exec = '' - go build -ldflags=" - -X $VERSION_PKG.Version=$VERSION - -X $VERSION_PKG.Build=$(build-id) - -X $VERSION_PKG.BuildDate=$(build-date) - -X $VERSION_PKG.BuiltBy=manual - " -o $BINARY_OUT . - ''; - scripts.clean.exec = '' - rm -rf ./dist/ ./out/ ./tmp/ - go clean - ''; - scripts.release.exec = '' - clean - goreleaser release - docker-build-release - ''; - scripts.lint.exec = "trunk check"; - scripts.fmt.exec = "trunk fmt"; - - scripts.docker-image.exec = "echo $DOCKER_REGISTRY/$DOCKER_ORG/$NAME"; - - scripts.docker-init.exec = '' - docker buildx create \ - --name $NAME \ - --platform $DOCKER_PLATFORMS - ''; - - scripts.docker-login.exec = "docker login $DOCKER_REGISTRY"; - - scripts.docker-build.exec = '' - platform=''${1:-linux} - output=''${2:-type=docker} - - docker-init - PROGRESS_NO_TRUNC=1 docker buildx build . \ - --tag $(docker-image):$VERSION \ - --tag $(docker-image):latest \ - --platform $platform \ - --builder $NAME \ - --build-arg VERSION=$VERSION \ - --build-arg BUILD=$(build-id) \ - --build-arg BUILD_DATE=$(build-date) \ - --build-arg BUILT_BY="Devenv Script" \ - --progress plain \ - --output $output - ''; - - scripts.docker-build-release.exec = "docker-build $DOCKER_PLATFORMS type=image,push=true"; - - enterShell = '' - echo "Welcome to the $NAME development environment." - echo -n "Golang version: $(go version | cut -d ' ' -f 3), " - echo -n "Goreleaser version: $(goreleaser --version | tail -n 9 | head -n 1 | cut -c 16-), " - echo -n "Docker CLI version: $(docker version -f {{.Client.Version}}), " - echo -n "Buildx version: $(docker buildx version | cut -d ' ' -f 2), " - echo "yt-dlp version: $(yt-dlp --version)" - ''; - - # https://devenv.sh/languages/ - languages.go.enable = true; - - # https://devenv.sh/pre-commit-hooks/ - pre-commit.hooks.staticcheck.enable = true; - pre-commit.hooks.hadolint.enable = true; - pre-commit.hooks.yamllint.enable = true; - - # https://devenv.sh/processes/ - processes.web.exec = "air"; - - # See full reference at https://devenv.sh/reference/options/ -} diff --git a/nix/package.nix b/nix/package.nix index 830c882..770a137 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -22,15 +22,6 @@ buildGoModule rec { "-X $VERSION_PKG.BuiltBy=nix" ]; - nativeBuildInputs = [ installShellFiles ]; - - postInstall = '' - installShellCompletion --cmd ${pname} \ - --zsh <($out/bin/${pname} completion zsh) \ - --bash <($out/bin/${pname} completion bash) \ - --fish <($out/bin/${pname} completion fish) - ''; - meta = with lib; { description = "Yet another yt-dlp web frontend written in Go."; homepage = "https://git.fifitido.net/apps/ytdl-web"; diff --git a/pkg/config/var.go b/pkg/config/var.go new file mode 100644 index 0000000..e70e5f7 --- /dev/null +++ b/pkg/config/var.go @@ -0,0 +1,104 @@ +package config + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" +) + +func getEnvKey(key string) string { + return fmt.Sprintf("YTDL_WEB_%s", strings.ReplaceAll(strings.ToUpper(key), ".", "_")) +} + +func String(key string, defaultValue string, usage string) func() string { + flagValue := flag.String(key, defaultValue, usage) + return func() string { + if flagValue != nil && *flagValue != defaultValue { + return *flagValue + } + + envValue, ok := os.LookupEnv(getEnvKey(key)) + if ok { + return envValue + } + + return defaultValue + } +} + +func Int(key string, defaultValue int, usage string) func() int { + flagValue := flag.Int(key, defaultValue, usage) + return func() int { + if flagValue != nil && *flagValue != defaultValue { + return *flagValue + } + + envValue, ok := os.LookupEnv(getEnvKey(key)) + if ok { + value, err := strconv.Atoi(envValue) + if err == nil { + return value + } + } + + return defaultValue + } +} + +func Bool(key string, defaultValue bool, usage string) func() bool { + flagValue := flag.Bool(key, defaultValue, usage) + return func() bool { + if flagValue != nil && *flagValue != defaultValue { + return *flagValue + } + + envValue, ok := os.LookupEnv(getEnvKey(key)) + if ok { + value, err := strconv.ParseBool(envValue) + if err == nil { + return value + } + } + + return defaultValue + } +} + +func StringSlice(key string, defaultValue []string, usage string) func() []string { + defaultString := strings.Join(defaultValue, ",") + flagValue := flag.String(key, defaultString, usage) + return func() []string { + if flagValue != nil && *flagValue != defaultString { + return strings.Split(*flagValue, ",") + } + + envValue, ok := os.LookupEnv(getEnvKey(key)) + if ok { + return strings.Split(envValue, ",") + } + + return defaultValue + } +} + +func Duration(key string, defaultValue time.Duration, usage string) func() time.Duration { + flagValue := flag.Duration(key, defaultValue, usage) + return func() time.Duration { + if flagValue != nil && *flagValue != defaultValue { + return *flagValue + } + + envValue, ok := os.LookupEnv(getEnvKey(key)) + if ok { + value, err := time.ParseDuration(envValue) + if err == nil { + return value + } + } + + return defaultValue + } +} diff --git a/pkg/server/routes.go b/pkg/routes/routes.go similarity index 97% rename from pkg/server/routes.go rename to pkg/routes/routes.go index 5b9318c..94cccbf 100644 --- a/pkg/server/routes.go +++ b/pkg/routes/routes.go @@ -1,4 +1,4 @@ -package server +package routes import ( "fmt" @@ -16,13 +16,15 @@ import ( "go.fifitido.net/ytdl-web/pkg/ytdl/metadata" ) -func Routes(r chi.Router) { +func Router() chi.Router { + r := chi.NewRouter() r.Get("/", home) r.Route("/download", func(r chi.Router) { r.Get("/", download) r.Head("/proxy", proxyDownload) r.Get("/proxy", proxyDownload) }) + return r } func renderPage(w http.ResponseWriter, r *http.Request, component templ.Component) { diff --git a/pkg/server/options.go b/pkg/server/options.go deleted file mode 100644 index 5cb1c51..0000000 --- a/pkg/server/options.go +++ /dev/null @@ -1,37 +0,0 @@ -package server - -import "log/slog" - -type Options struct { - ListenAddr string - ListenPort int - Logger *slog.Logger -} - -func DefaultOptions() *Options { - return &Options{ - ListenAddr: "127.0.0.1", - ListenPort: 8080, - Logger: slog.Default(), - } -} - -type Option func(*Options) - -func WithListenAddr(addr string) Option { - return func(o *Options) { - o.ListenAddr = addr - } -} - -func WithListenPort(port int) Option { - return func(o *Options) { - o.ListenPort = port - } -} - -func WithLogger(logger *slog.Logger) Option { - return func(o *Options) { - o.Logger = logger - } -} diff --git a/pkg/server/server.go b/pkg/server/server.go deleted file mode 100644 index f7bb343..0000000 --- a/pkg/server/server.go +++ /dev/null @@ -1,35 +0,0 @@ -package server - -import ( - "fmt" - "log/slog" - "net/http" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - slogchi "github.com/samber/slog-chi" - "go.fifitido.net/ytdl-web/pkg/serverctx" -) - -func ListenAndServe(options ...Option) error { - opts := DefaultOptions() - for _, opt := range options { - opt(opts) - } - - r := chi.NewRouter() - - r.Use( - serverctx.Middleware, - middleware.RequestID, - middleware.RealIP, - slogchi.New(opts.Logger), - middleware.Recoverer, - ) - - r.Group(Routes) - - listenAddr := fmt.Sprintf("%s:%d", opts.ListenAddr, opts.ListenPort) - opts.Logger.Info("Starting HTTP server", slog.String("addr", opts.ListenAddr), slog.Int("port", opts.ListenPort)) - return http.ListenAndServe(listenAddr, r) -} diff --git a/pkg/serverctx/context.go b/pkg/serverctx/context.go index 902c743..cb625b5 100644 --- a/pkg/serverctx/context.go +++ b/pkg/serverctx/context.go @@ -8,18 +8,30 @@ import ( type contextKey string const ( - isHTTPS contextKey = "isHTTPS" + isHTTPSKey contextKey = "isHTTPS" + basePathKey contextKey = "basePath" ) -func Middleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - isHttps := r.URL.Scheme == "https" || r.Header.Get("X-Forwarded-Proto") == "https" - ctx := context.WithValue(r.Context(), isHTTPS, isHttps) - next.ServeHTTP(w, r.WithContext(ctx)) - }) +func Middleware(basePath string) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + isHttps := r.URL.Scheme == "https" || r.Header.Get("X-Forwarded-Proto") == "https" + ctx := context.WithValue(r.Context(), isHTTPSKey, isHttps) + ctx = context.WithValue(ctx, basePathKey, basePath) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } } func IsHTTPS(ctx context.Context) bool { - isHttps, ok := ctx.Value(isHTTPS).(bool) + isHttps, ok := ctx.Value(isHTTPSKey).(bool) return ok && isHttps } + +func BasePath(ctx context.Context) string { + basePath, ok := ctx.Value(basePathKey).(string) + if !ok { + return "" + } + return basePath +} diff --git a/pkg/views/downloads.templ b/pkg/views/downloads.templ index 2c5297e..095f339 100644 --- a/pkg/views/downloads.templ +++ b/pkg/views/downloads.templ @@ -31,7 +31,7 @@ templ videoFormatView(vm *DownloadsViewModel, vidIndex int, format metadata.Form Download (proxied) @@ -106,8 +106,8 @@ templ Downloads(vm *DownloadsViewModel) {

{ *vm.Meta.Title }

{ vm.Url }

here.

-
+
@@ -48,7 +48,7 @@ templ Home(err *Error) { if err.RetryUrl != nil {