diff --git a/.github/workflows/check-semgrep.yml b/.github/workflows/check-semgrep.yml new file mode 100644 index 00000000..c2d6bef7 --- /dev/null +++ b/.github/workflows/check-semgrep.yml @@ -0,0 +1,33 @@ +name: Check-Semgrep + +on: + workflow_call: + workflow_dispatch: + schedule: + - cron: 0 0 * * * + push: + branches: + - "**" + tags: + - "v*.*.*" + paths-ignore: + - "**/*.md" + - "**/*.yaml" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + - "**/*.yaml" + +jobs: + semgrep: + name: Scan + runs-on: ubuntu-24.04 + container: + image: semgrep/semgrep:latest + continue-on-error: true + if: (github.actor != 'dependabot[bot]') + steps: + - uses: actions/checkout@v4 + - run: semgrep ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..0529bb86 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + workflow_call: + workflow_dispatch: + push: + branches: + - "**" + tags: + - "v*.*.*" + paths-ignore: + - "**/*.md" + - "**/*.yaml" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + - "**/*.yaml" + +jobs: + golangci-lint: + name: Lint + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + + - name: Go test + run: | + go test -v -timeout 30s -count=1 ./... + + - name: Run Linter + uses: golangci/golangci-lint-action@v8 + with: + version: latest + args: --color always + + - name: Run Fix Linter + uses: golangci/golangci-lint-action@v8 + if: ${{ failure() }} + with: + install-mode: none + args: --fix --color always + + - name: Auto Fix Diff Content + if: ${{ failure() }} + run: | + git diff --color=always + exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f1fc415..96b418f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version-file: "go.mod" - name: Build targets uses: zijiren233/go-build-action@v1 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..d75c3d2a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,133 @@ +version: "2" + +run: + go: "1.24" + relative-path-mode: gomod + modules-download-mode: readonly + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + +linters: + default: none + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - canonicalheader + - containedctx + - copyloopvar + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exptostd + - fatcontext + - forbidigo + - ginkgolinter + - gocheckcompilerdirectives + - gocritic + - gocyclo + - goprintffuncname + - gosec + - govet + - iface + - importas + - inamedparam + - ineffassign + - intrange + - loggercheck + - mirror + - misspell + - musttag + - nakedret + - noctx + - nolintlint + - nosprintfhostport + - perfsprint + - prealloc + - predeclared + - promlinter + - protogetter + - reassign + - revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - staticcheck + - testpackage + - thelper + - tparallel + - unconvert + - unparam + - unused + - usestdlibvars + - usetesting + - wastedassign + - whitespace + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ + settings: + copyloopvar: + check-alias: true + cyclop: + max-complexity: 15 + errcheck: + check-type-assertions: true + forbidigo: + analyze-types: true + prealloc: + for-loops: true + staticcheck: + dot-import-whitelist: [] + http-status-code-whitelist: [] + usestdlibvars: + time-month: true + time-layout: true + crypto-hash: true + default-rpc-path: true + sql-isolation-level: true + tls-signature-scheme: true + constant-kind: true + usetesting: + os-temp-dir: true + gosec: + excludes: + - G404 + +formatters: + enable: + - gci + - gofmt + - gofumpt + - golines + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ + settings: + gofmt: + rewrite-rules: + - pattern: "interface{}" + replacement: "any" + - pattern: "a[b:len(a)]" + replacement: "a[b:]" + gofumpt: + extra-rules: true + golines: + shorten-comments: true diff --git a/cmd/admin/add.go b/cmd/admin/add.go index 2ce4dbbb..b6c9465c 100644 --- a/cmd/admin/add.go +++ b/cmd/admin/add.go @@ -2,8 +2,8 @@ package admin import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -16,11 +16,11 @@ var AddCmd = &cobra.Command{ Short: "add admin by user id", Long: `add admin by user id`, PreRunE: func(cmd *cobra.Command, _ []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { @@ -28,14 +28,14 @@ var AddCmd = &cobra.Command{ } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s", err) + log.Errorf("get user failed: %s", err) return nil } if err := db.AddAdmin(u); err != nil { - fmt.Printf("add admin failed: %s", err) + log.Errorf("add admin failed: %s", err) return nil } - fmt.Printf("add admin success: %s\n", u.Username) + log.Infof("add admin success: %s\n", u.Username) return nil }, } diff --git a/cmd/admin/delete.go b/cmd/admin/delete.go index e19ddf1a..2f0e2da9 100644 --- a/cmd/admin/delete.go +++ b/cmd/admin/delete.go @@ -2,8 +2,8 @@ package admin import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,27 +13,27 @@ var RemoveCmd = &cobra.Command{ Use: "remove", Short: "remove", Long: `remove admin`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s", err) + log.Errorf("get user failed: %s", err) return nil } if err := db.RemoveAdmin(u); err != nil { - fmt.Printf("remove admin failed: %s", err) + log.Errorf("remove admin failed: %s", err) return nil } - fmt.Printf("remove admin success: %s\n", u.Username) + log.Infof("remove admin success: %s\n", u.Username) return nil }, } diff --git a/cmd/admin/show.go b/cmd/admin/show.go index ed40a99a..a02ae547 100644 --- a/cmd/admin/show.go +++ b/cmd/admin/show.go @@ -1,8 +1,7 @@ package admin import ( - "fmt" - + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -12,20 +11,20 @@ var ShowCmd = &cobra.Command{ Use: "show", Short: "show admin", Long: `show admin`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, _ []string) error { admins, err := db.GetAdmins() if err != nil { - fmt.Printf("get admins failed: %s\n", err.Error()) + log.Errorf("get admins failed: %s\n", err.Error()) } for _, admin := range admins { - fmt.Printf("id: %s\tusername: %s\n", admin.ID, admin.Username) + log.Infof("id: %s\tusername: %s\n", admin.ID, admin.Username) } return nil }, diff --git a/cmd/root.go b/cmd/root.go index 3bc8f22c..6fd6c095 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,7 +25,7 @@ var RootCmd = &cobra.Command{ Use: "synctv", Short: "synctv", Long: `synctv https://github.com/synctv-org/synctv`, - PersistentPreRun: func(cmd *cobra.Command, args []string) { + PersistentPreRun: func(_ *cobra.Command, _ []string) { prefix := flags.EnvPrefix if !flags.SkipEnvFlag { s, ok := os.LookupEnv("ENV_NO_PREFIX") @@ -94,17 +94,22 @@ func Execute() { } func init() { - RootCmd.PersistentFlags().BoolVar(&flags.Global.Dev, "dev", version.Version == "dev", "start with dev mode") + RootCmd.PersistentFlags(). + BoolVar(&flags.Global.Dev, "dev", version.Version == "dev", "start with dev mode") RootCmd.PersistentFlags().BoolVar(&flags.Global.LogStd, "log-std", true, "log to std") - RootCmd.PersistentFlags().BoolVar(&flags.EnvNoPrefix, "env-no-prefix", false, "env no SYNCTV_ prefix") + RootCmd.PersistentFlags(). + BoolVar(&flags.EnvNoPrefix, "env-no-prefix", false, "env no SYNCTV_ prefix") RootCmd.PersistentFlags().BoolVar(&flags.SkipEnvFlag, "skip-env-flag", true, "skip env flag") - RootCmd.PersistentFlags().StringVar(&flags.Global.GitHubBaseURL, "github-base-url", "https://api.github.com/", "github api base url") + RootCmd.PersistentFlags(). + StringVar(&flags.Global.GitHubBaseURL, "github-base-url", "https://api.github.com/", "github api base url") home, err := homedir.Dir() if err != nil { home = "~" } - RootCmd.PersistentFlags().StringVar(&flags.Global.DataDir, "data-dir", filepath.Join(home, ".synctv"), "data dir") - RootCmd.PersistentFlags().BoolVar(&flags.Global.ForceAutoMigrate, "force-auto-migrate", version.Version == "dev", "force auto migrate") + RootCmd.PersistentFlags(). + StringVar(&flags.Global.DataDir, "data-dir", filepath.Join(home, ".synctv"), "data dir") + RootCmd.PersistentFlags(). + BoolVar(&flags.Global.ForceAutoMigrate, "force-auto-migrate", version.Version == "dev", "force auto migrate") } func init() { diff --git a/cmd/root/add.go b/cmd/root/add.go index d22bfdd2..fa14946f 100644 --- a/cmd/root/add.go +++ b/cmd/root/add.go @@ -2,8 +2,8 @@ package root import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,27 +13,27 @@ var AddCmd = &cobra.Command{ Use: "add", Short: "add root by user id", Long: `add root by user id`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s", err) + log.Errorf("get user failed: %s", err) return nil } if err := db.AddRoot(u); err != nil { - fmt.Printf("add root failed: %s", err) + log.Errorf("add root failed: %s", err) return nil } - fmt.Printf("add root success: %s\n", u.Username) + log.Infof("add root success: %s\n", u.Username) return nil }, } diff --git a/cmd/root/delete.go b/cmd/root/delete.go index 3bc9b673..2293473e 100644 --- a/cmd/root/delete.go +++ b/cmd/root/delete.go @@ -2,8 +2,8 @@ package root import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,27 +13,27 @@ var RemoveCmd = &cobra.Command{ Use: "remove", Short: "remove", Long: `remove root`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s", err) + log.Errorf("get user failed: %s", err) return nil } if err := db.RemoveRoot(u); err != nil { - fmt.Printf("remove root failed: %s", err) + log.Errorf("remove root failed: %s", err) return nil } - fmt.Printf("remove root success: %s\n", u.Username) + log.Infof("remove root success: %s\n", u.Username) return nil }, } diff --git a/cmd/root/show.go b/cmd/root/show.go index 4445ab68..c635444c 100644 --- a/cmd/root/show.go +++ b/cmd/root/show.go @@ -1,8 +1,7 @@ package root import ( - "fmt" - + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -12,17 +11,17 @@ var ShowCmd = &cobra.Command{ Use: "show", Short: "show root", Long: `show root`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, _ []string) error { roots := db.GetRoots() for _, root := range roots { - fmt.Printf("id: %s\tusername: %s\n", root.ID, root.Username) + log.Infof("id: %s\tusername: %s\n", root.ID, root.Username) } return nil }, diff --git a/cmd/self-update.go b/cmd/self-update.go index 5078bd91..d7dd970d 100644 --- a/cmd/self-update.go +++ b/cmd/self-update.go @@ -20,15 +20,15 @@ var SelfUpdateCmd = &cobra.Command{ Use: "self-update", Short: "self-update", Long: SelfUpdateLong, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, - ).Run() + ).Run(cmd.Context()) }, RunE: SelfUpdate, } -func SelfUpdate(cmd *cobra.Command, args []string) error { +func SelfUpdate(cmd *cobra.Command, _ []string) error { v, err := version.NewVersionInfo(version.WithBaseURL(flags.Global.GitHubBaseURL)) if err != nil { log.Errorf("get version info error: %v", err) diff --git a/cmd/server.go b/cmd/server.go index bc213de9..a4bbc4b5 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -22,8 +22,8 @@ var ServerCmd = &cobra.Command{ Use: "server", Short: "Start synctv-server", Long: `Start synctv-server`, - PreRunE: func(cmd *cobra.Command, args []string) error { - boot := bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + boot := bootstrap.New().Add( bootstrap.InitSysNotify, bootstrap.InitConfig, bootstrap.InitGinMode, @@ -38,13 +38,16 @@ var ServerCmd = &cobra.Command{ if !flags.Server.DisableUpdateCheck { boot.Add(bootstrap.InitCheckUpdate) } - return boot.Run() + return boot.Run(cmd.Context()) }, Run: Server, } -func setupAddresses() (tcpHTTPAddr *net.TCPAddr, tcpRTMPAddr *net.TCPAddr, err error) { - tcpHTTPAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", conf.Conf.Server.HTTP.Listen, conf.Conf.Server.HTTP.Port)) +func setupAddresses() (tcpHTTPAddr, tcpRTMPAddr *net.TCPAddr, err error) { + tcpHTTPAddr, err = net.ResolveTCPAddr( + "tcp", + fmt.Sprintf("%s:%d", conf.Conf.Server.HTTP.Listen, conf.Conf.Server.HTTP.Port), + ) if err != nil { return nil, nil, err } @@ -57,7 +60,10 @@ func setupAddresses() (tcpHTTPAddr *net.TCPAddr, tcpRTMPAddr *net.TCPAddr, err e conf.Conf.Server.RTMP.Port = conf.Conf.Server.HTTP.Port } - tcpRTMPAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", conf.Conf.Server.RTMP.Listen, conf.Conf.Server.RTMP.Port)) + tcpRTMPAddr, err = net.ResolveTCPAddr( + "tcp", + fmt.Sprintf("%s:%d", conf.Conf.Server.RTMP.Listen, conf.Conf.Server.RTMP.Port), + ) return } @@ -66,7 +72,11 @@ func startHTTPServer(e *gin.Engine, listener net.Listener) { case conf.Conf.Server.HTTP.CertPath != "" && conf.Conf.Server.HTTP.KeyPath != "": go func() { srv := http.Server{Handler: e.Handler(), ReadHeaderTimeout: 3 * time.Second} - err := srv.ServeTLS(listener, conf.Conf.Server.HTTP.CertPath, conf.Conf.Server.HTTP.KeyPath) + err := srv.ServeTLS( + listener, + conf.Conf.Server.HTTP.CertPath, + conf.Conf.Server.HTTP.KeyPath, + ) if err != nil { log.Panicf("http server error: %v", err) } @@ -84,7 +94,7 @@ func startHTTPServer(e *gin.Engine, listener net.Listener) { } } -func Server(cmd *cobra.Command, args []string) { +func Server(_ *cobra.Command, _ []string) { tcpHTTPAddr, tcpRTMPAddr, err := setupAddresses() if err != nil { log.Panic(err) @@ -161,10 +171,16 @@ func Server(cmd *cobra.Command, args []string) { func init() { RootCmd.AddCommand(ServerCmd) - ServerCmd.PersistentFlags().BoolVar(&flags.Server.DisableUpdateCheck, "disable-update-check", false, "disable update check") - ServerCmd.PersistentFlags().BoolVar(&flags.Server.DisableWeb, "disable-web", false, "disable web") - ServerCmd.PersistentFlags().BoolVar(&flags.Server.DisableLogColor, "disable-log-color", false, "disable log color") - ServerCmd.PersistentFlags().StringVar(&flags.Server.WebPath, "web-path", "", "if not set, use embed web") - ServerCmd.PersistentFlags().BoolVar(&flags.Server.SkipConfig, "skip-config", false, "skip config") - ServerCmd.PersistentFlags().BoolVar(&flags.Server.SkipEnvConfig, "skip-env-config", false, "skip env config") + ServerCmd.PersistentFlags(). + BoolVar(&flags.Server.DisableUpdateCheck, "disable-update-check", false, "disable update check") + ServerCmd.PersistentFlags(). + BoolVar(&flags.Server.DisableWeb, "disable-web", false, "disable web") + ServerCmd.PersistentFlags(). + BoolVar(&flags.Server.DisableLogColor, "disable-log-color", false, "disable log color") + ServerCmd.PersistentFlags(). + StringVar(&flags.Server.WebPath, "web-path", "", "if not set, use embed web") + ServerCmd.PersistentFlags(). + BoolVar(&flags.Server.SkipConfig, "skip-config", false, "skip config") + ServerCmd.PersistentFlags(). + BoolVar(&flags.Server.SkipEnvConfig, "skip-env-config", false, "skip env config") } diff --git a/cmd/setting/set.go b/cmd/setting/set.go index e586c69f..0c7b7192 100644 --- a/cmd/setting/set.go +++ b/cmd/setting/set.go @@ -2,8 +2,8 @@ package setting import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/settings" @@ -13,15 +13,15 @@ var SetCmd = &cobra.Command{ Use: "set", Short: "set setting", Long: `set setting`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, bootstrap.InitSetting, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) != 2 { return errors.New("args length must be 2") } @@ -31,9 +31,9 @@ var SetCmd = &cobra.Command{ } err := s.SetString(args[1]) if err != nil { - fmt.Printf("set setting %s error: %v\n", args[0], err) + log.Errorf("set setting %s error: %v\n", args[0], err) } - fmt.Printf("set setting success:\n%s: %v\n", args[0], s.Interface()) + log.Infof("set setting success:\n%s: %v\n", args[0], s.Interface()) return nil }, } diff --git a/cmd/setting/show.go b/cmd/setting/show.go index a689e0ab..be3abe92 100644 --- a/cmd/setting/show.go +++ b/cmd/setting/show.go @@ -14,15 +14,15 @@ var ShowCmd = &cobra.Command{ Use: "show", Short: "show setting", Long: `show setting`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, bootstrap.InitSetting, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, _ []string) error { m := make(map[model.SettingGroup]map[string]any) for g, s := range settings.GroupSettings { if _, ok := m[g]; !ok { diff --git a/cmd/user/ban.go b/cmd/user/ban.go index a92d3a2f..2eff1560 100644 --- a/cmd/user/ban.go +++ b/cmd/user/ban.go @@ -2,8 +2,8 @@ package user import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,28 +13,28 @@ var BanCmd = &cobra.Command{ Use: "ban", Short: "ban user with user id", Long: "ban user with user id", - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s\n", err) + log.Errorf("get user failed: %s\n", err) return nil } err = db.BanUser(u) if err != nil { - fmt.Printf("ban user failed: %s\n", err) + log.Errorf("ban user failed: %s\n", err) return nil } - fmt.Printf("ban user success: %s\n", u.Username) + log.Infof("ban user success: %s\n", u.Username) return nil }, } diff --git a/cmd/user/delete.go b/cmd/user/delete.go index a611fc60..36d0d2a9 100644 --- a/cmd/user/delete.go +++ b/cmd/user/delete.go @@ -2,8 +2,8 @@ package user import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,23 +13,23 @@ var DeleteCmd = &cobra.Command{ Use: "delete", Short: "delete", Long: `delete user`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.LoadAndDeleteUserByID(args[0]) if err != nil { - fmt.Printf("delete user failed: %s\n", err) + log.Errorf("delete user failed: %s\n", err) return nil } - fmt.Printf("delete user success: %s\n", u.Username) + log.Infof("delete user success: %s\n", u.Username) return nil }, } diff --git a/cmd/user/search.go b/cmd/user/search.go index bbe83713..dcf52a27 100644 --- a/cmd/user/search.go +++ b/cmd/user/search.go @@ -2,8 +2,8 @@ package user import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,14 +13,14 @@ var SearchCmd = &cobra.Command{ Use: "search", Short: "search user by id or username", Long: `search user by id or username`, - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id or username") } @@ -29,11 +29,17 @@ var SearchCmd = &cobra.Command{ return err } if len(us) == 0 { - fmt.Println("user not found") + log.Infof("user not found") return nil } for _, u := range us { - fmt.Printf("id: %s\tusername: %s\tcreated_at: %s\trole: %s\n", u.ID, u.Username, u.CreatedAt, u.Role) + log.Infof( + "id: %s\tusername: %s\tcreated_at: %s\trole: %s\n", + u.ID, + u.Username, + u.CreatedAt, + u.Role, + ) } return nil }, diff --git a/cmd/user/unban.go b/cmd/user/unban.go index d2319dd4..15c25199 100644 --- a/cmd/user/unban.go +++ b/cmd/user/unban.go @@ -2,8 +2,8 @@ package user import ( "errors" - "fmt" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" @@ -13,28 +13,28 @@ var UnbanCmd = &cobra.Command{ Use: "unban", Short: "unban user with user id", Long: "unban user with user id", - PreRunE: func(cmd *cobra.Command, args []string) error { - return bootstrap.New(bootstrap.WithContext(cmd.Context())).Add( + PreRunE: func(cmd *cobra.Command, _ []string) error { + return bootstrap.New().Add( bootstrap.InitStdLog, bootstrap.InitConfig, bootstrap.InitDatabase, - ).Run() + ).Run(cmd.Context()) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing user id") } u, err := db.GetUserByID(args[0]) if err != nil { - fmt.Printf("get user failed: %s\n", err) + log.Errorf("get user failed: %s\n", err) return nil } err = db.UnbanUser(u) if err != nil { - fmt.Printf("unban user failed: %s", err) + log.Errorf("unban user failed: %s", err) return nil } - fmt.Printf("unban user success: %s\n", u.Username) + log.Infof("unban user success: %s\n", u.Username) return nil }, } diff --git a/cmd/version.go b/cmd/version.go index 01325fd5..1197f9d2 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -9,11 +9,12 @@ import ( "github.com/synctv-org/synctv/internal/version" ) +//nolint:forbidigo var VersionCmd = &cobra.Command{ Use: "version", Short: "Print the version number of Sync TV Server", Long: `All software has versions. This is Sync TV Server's`, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Printf("synctv %s\n", version.Version) fmt.Printf("- git/commit: %s\n", version.GitCommit) fmt.Printf("- os/platform: %s\n", runtime.GOOS) diff --git a/go.mod b/go.mod index 2238534d..048be0c6 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/synctv-org/synctv -go 1.23.0 +go 1.24 replace github.com/synctv-org/vendors => ./vendors @@ -10,19 +10,19 @@ require ( github.com/caarlos0/env/v9 v9.0.0 github.com/cavaliergopher/grab/v3 v3.0.1 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 - github.com/emersion/go-smtp v0.21.3 - github.com/gin-contrib/cors v1.7.3 - github.com/gin-gonic/gin v1.10.0 + github.com/emersion/go-smtp v0.22.0 + github.com/gin-contrib/cors v1.7.5 + github.com/gin-gonic/gin v1.10.1 github.com/glebarez/sqlite v1.11.0 github.com/go-kratos/aegis v0.2.0 - github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250314165958-d9aa7ff19541 - github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250314165958-d9aa7ff19541 + github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250527152916-d6f5f00cf562 + github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250527152916-d6f5f00cf562 github.com/go-kratos/kratos/v2 v2.8.4 - github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/google/go-github/v56 v56.0.0 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 - github.com/hashicorp/consul/api v1.31.2 + github.com/hashicorp/consul/api v1.32.1 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.6.3 github.com/joho/godotenv v1.5.1 @@ -31,6 +31,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mojocn/base64Captcha v1.3.8 github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/soheilhy/cmux v0.1.5 github.com/spf13/cobra v1.9.1 @@ -44,48 +45,47 @@ require ( github.com/zijiren233/livelib v0.3.3 github.com/zijiren233/stream v0.5.3 github.com/zijiren233/yaml-comment v0.2.2 - go.etcd.io/etcd/client/v3 v3.5.19 - golang.org/x/crypto v0.36.0 - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 - golang.org/x/oauth2 v0.28.0 - google.golang.org/grpc v1.71.0 - google.golang.org/protobuf v1.36.5 + go.etcd.io/etcd/client/v3 v3.6.0 + golang.org/x/crypto v0.38.0 + golang.org/x/oauth2 v0.30.0 + google.golang.org/grpc v1.72.2 + google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 - gorm.io/driver/postgres v1.5.11 + gorm.io/driver/postgres v1.6.0 gorm.io/driver/sqlite v1.5.7 - gorm.io/gorm v1.25.12 + gorm.io/gorm v1.30.0 ) require ( - cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/bytedance/sonic v1.13.1 // indirect + github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cloudflare/circl v1.6.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/gin-contrib/sse v1.0.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-playground/form/v4 v4.2.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.25.0 // indirect - github.com/go-sql-driver/mysql v1.9.0 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/go-sql-driver/mysql v1.9.2 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect github.com/google/wire v0.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -98,7 +98,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/pgx/v5 v5.7.5 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -107,37 +107,37 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.24 // indirect + github.com/mattn/go-sqlite3 v1.14.28 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/oklog/run v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/refraction-networking/utls v1.6.7 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/refraction-networking/utls v1.7.3 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - go.etcd.io/etcd/api/v3 v3.5.19 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.19 // indirect + github.com/ugorji/go/codec v1.2.14 // indirect + go.etcd.io/etcd/api/v3 v3.6.0 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/arch v0.15.0 // indirect - golang.org/x/image v0.25.0 // indirect - golang.org/x/net v0.37.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect + golang.org/x/image v0.27.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - modernc.org/libc v1.61.13 // indirect + modernc.org/libc v1.65.8 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.8.2 // indirect - modernc.org/sqlite v1.36.1 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.37.1 // indirect ) replace github.com/tetratelabs/wazero => github.com/tetratelabs/wazero v1.8.1 diff --git a/go.sum b/go.sum index b147091e..508d229c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= -cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= +cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Boostport/mjml-go v0.15.0 h1:t4AJt1WI5KpijaQrXeYr03rml//NGsQ9xgnkzluGgvA= @@ -24,8 +24,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= -github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= @@ -36,13 +36,13 @@ github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYpt github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 h1:boJj011Hh+874zpIySeApCX4GeOjPl9qhRF3QuIZq+Q= -github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -54,11 +54,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY= -github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.22.0 h1:/d3HWxkZZ4riB+0kzfoODh9X+xyCrLEezMnAAa1LEMU= +github.com/emersion/go-smtp v0.22.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ= github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= @@ -67,14 +66,14 @@ github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2T github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/gin-contrib/cors v1.7.3 h1:hV+a5xp8hwJoTw7OY+a70FsL8JkVVFTXw9EcfrYUdns= -github.com/gin-contrib/cors v1.7.3/go.mod h1:M3bcKZhxzsvI+rlRSkkxHyljJt1ESd93COUvemZ79j4= -github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= -github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk= +github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= @@ -84,10 +83,10 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ= github.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI= -github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250314165958-d9aa7ff19541 h1:hew9nMKUssGGU1f6Q8U+LUlNy0xtZ/MD07K8BtVraGU= -github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250314165958-d9aa7ff19541/go.mod h1:OpFw/FRkeh19tGNpVtm1Yiy+y2EqjXLnZQwCuUaOExY= -github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250314165958-d9aa7ff19541 h1:Qgj7B+zeY+MRIYYnC6xNgoP06+8rKj8JGMqb0vOVuGc= -github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250314165958-d9aa7ff19541/go.mod h1:TrIH4HDRTR1RAx9WVGZ8f1b+1wPdsoqVc0t7l7PtAGE= +github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250527152916-d6f5f00cf562 h1:PVWOn77FfwOYT4veUVB1zCtWNZq2SbKcxYO47BhmgQQ= +github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20250527152916-d6f5f00cf562/go.mod h1:I3L2JB86WBDlvBEICeJ39X/0KF0JJ4fkfbSg8LRSfRU= +github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250527152916-d6f5f00cf562 h1:wjCLWL0wtI05dIbFOt+BkE3hUMHNiiWFrgxltXoY1UQ= +github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20250527152916-d6f5f00cf562/go.mod h1:4/85gQIHVmmeAW7WrQv4gAEys+8cSvj+T3Mt9YqNuLU= github.com/go-kratos/kratos/v2 v2.8.4 h1:eIJLE9Qq9WSoKx+Buy2uPyrahtF/lPh+Xf4MTpxhmjs= github.com/go-kratos/kratos/v2 v2.8.4/go.mod h1:mq62W2101a5uYyRxe+7IdWubu7gZCGYqSNKwGFiiRcw= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -106,11 +105,11 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= -github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= -github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= +github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= +github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= @@ -118,8 +117,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -143,15 +142,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v56 v56.0.0 h1:TysL7dMa/r7wsQi44BjqlwaHvwlFlqkK8CtBWCX3gb4= github.com/google/go-github/v56 v56.0.0/go.mod h1:D8cdcX98YWJvi7TLo7zM4/h8ZTx6u6fwGEkCdisopo0= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA= -github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -161,8 +161,10 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/consul/api v1.31.2 h1:NicObVJHcCmyOIl7Z9iHPvvFrocgTYo9cITSGg0/7pw= -github.com/hashicorp/consul/api v1.31.2/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= +github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -210,8 +212,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= -github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= +github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -261,8 +263,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= @@ -289,8 +291,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -317,12 +319,12 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= -github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= +github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo= +github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -340,9 +342,8 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -351,7 +352,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550= @@ -359,8 +359,8 @@ github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw= +github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= @@ -384,12 +384,12 @@ github.com/zijiren233/stream v0.5.3 h1:OirMdxbufExBcMKSQiyPnOUWM3bIXykVuLWjH/SeG github.com/zijiren233/stream v0.5.3/go.mod h1:iIrOm3qgIepQFmptD/HDY+YzamSSzQOtPjpVcK7FCOw= github.com/zijiren233/yaml-comment v0.2.2 h1:5ghs8huXFVb/kWCi66P+xbXq0GnOE2XVCnhaWd7mTs8= github.com/zijiren233/yaml-comment v0.2.2/go.mod h1:YksA19x5zWKaz8c/bJdSuVRo2G11FYk2/lDVcjYnYI4= -go.etcd.io/etcd/api/v3 v3.5.19 h1:w3L6sQZGsWPuBxRQ4m6pPP3bVUtV8rjW033EGwlr0jw= -go.etcd.io/etcd/api/v3 v3.5.19/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U= -go.etcd.io/etcd/client/pkg/v3 v3.5.19 h1:9VsyGhg0WQGjDWWlDI4VuaS9PZJGNbPkaHEIuLwtixk= -go.etcd.io/etcd/client/pkg/v3 v3.5.19/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0= -go.etcd.io/etcd/client/v3 v3.5.19 h1:+4byIz6ti3QC28W0zB0cEZWwhpVHXdrKovyycJh1KNo= -go.etcd.io/etcd/client/v3 v3.5.19/go.mod h1:FNzyinmMIl0oVsty1zA3hFeUrxXI/JpEnz4sG+POzjU= +go.etcd.io/etcd/api/v3 v3.6.0 h1:vdbkcUBGLf1vfopoGE/uS3Nv0KPyIpUV/HM6w9yx2kM= +go.etcd.io/etcd/api/v3 v3.6.0/go.mod h1:Wt5yZqEmxgTNJGHob7mTVBJDZNXiHPtXTcPab37iFOw= +go.etcd.io/etcd/client/pkg/v3 v3.6.0 h1:nchnPqpuxvv3UuGGHaz0DQKYi5EIW5wOYsgUNRc365k= +go.etcd.io/etcd/client/pkg/v3 v3.6.0/go.mod h1:Jv5SFWMnGvIBn8o3OaBq/PnT0jjsX8iNokAUessNjoA= +go.etcd.io/etcd/client/v3 v3.6.0 h1:/yjKzD+HW5v/3DVj9tpwFxzNbu8hjcKID183ug9duWk= +go.etcd.io/etcd/client/v3 v3.6.0/go.mod h1:Jzk/Knqe06pkOZPHXsQ0+vNDvMQrgIqJ0W8DwPdMJMg= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= @@ -408,8 +408,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= -golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -419,13 +419,13 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= -golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= -golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -454,11 +454,11 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -471,8 +471,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -503,8 +503,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -524,8 +524,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -535,19 +535,19 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -555,8 +555,8 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -568,41 +568,40 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/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= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= -gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= -gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= -modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= -modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= -modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= -modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= -modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= +modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= +modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= +modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8= +modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.65.8 h1:7PXRJai0TXZ8uNA3srsmYzmTyrLoHImV5QxHeni108Q= +modernc.org/libc v1.65.8/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= -modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= -modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs= +modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/bootstrap/config.go b/internal/bootstrap/config.go index 0152c67c..773a1f28 100644 --- a/internal/bootstrap/config.go +++ b/internal/bootstrap/config.go @@ -8,18 +8,17 @@ import ( "github.com/caarlos0/env/v9" log "github.com/sirupsen/logrus" - "github.com/synctv-org/synctv/cmd/flags" "github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/utils" ) -func InitDefaultConfig(ctx context.Context) error { +func InitDefaultConfig(_ context.Context) error { conf.Conf = conf.DefaultConfig() return nil } -func InitConfig(ctx context.Context) (err error) { +func InitConfig(_ context.Context) (err error) { if flags.Server.SkipConfig && flags.Server.SkipEnvConfig { log.Fatal("skip config and skip env at the same time") return errors.New("skip config and skip env at the same time") diff --git a/internal/bootstrap/db.go b/internal/bootstrap/db.go index 58560f05..62467833 100644 --- a/internal/bootstrap/db.go +++ b/internal/bootstrap/db.go @@ -20,7 +20,7 @@ import ( "gorm.io/gorm/logger" ) -func InitDatabase(ctx context.Context) (err error) { +func InitDatabase(_ context.Context) (err error) { dialector, err := createDialector(conf.Conf.Database) if err != nil { log.Fatalf("failed to create dialector: %s", err.Error()) @@ -42,9 +42,12 @@ func InitDatabase(ctx context.Context) (err error) { if err != nil { log.Fatalf("failed to get sqlDB: %s", err.Error()) } - err = sysnotify.RegisterSysNotifyTask(0, sysnotify.NewSysNotifyTask("database", sysnotify.NotifyTypeEXIT, func() error { - return sqlDB.Close() - })) + err = sysnotify.RegisterSysNotifyTask( + 0, + sysnotify.NewSysNotifyTask("database", sysnotify.NotifyTypeEXIT, func() error { + return sqlDB.Close() + }), + ) if err != nil { log.Fatalf("failed to register sysnotify task: %s", err.Error()) } @@ -58,10 +61,12 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err var dsn string switch dbConf.Type { case conf.DatabaseTypeMysql: - if dbConf.CustomDSN != "" { + switch { + case dbConf.CustomDSN != "": dsn = dbConf.CustomDSN - } else if dbConf.Port == 0 { - dsn = fmt.Sprintf("%s:%s@unix(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&interpolateParams=true&tls=%s", + case dbConf.Port == 0: + dsn = fmt.Sprintf( + "%s:%s@unix(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&interpolateParams=true&tls=%s", dbConf.User, dbConf.Password, dbConf.Host, @@ -69,8 +74,9 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err dbConf.SslMode, ) log.Infof("mysql database: %s", dbConf.Host) - } else { - dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&interpolateParams=true&tls=%s", + default: + dsn = fmt.Sprintf( + "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&interpolateParams=true&tls=%s", dbConf.User, dbConf.Password, dbConf.Host, @@ -89,12 +95,13 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err SkipInitializeWithVersion: false, }) case conf.DatabaseTypeSqlite3: - if dbConf.CustomDSN != "" { + switch { + case dbConf.CustomDSN != "": dsn = dbConf.CustomDSN - } else if dbConf.Name == "memory" || strings.HasPrefix(dbConf.Name, ":memory:") { + case dbConf.Name == "memory" || strings.HasPrefix(dbConf.Name, ":memory:"): dsn = "file::memory:?cache=shared&_journal_mode=WAL&_vacuum=incremental&_pragma=foreign_keys(1)" log.Infof("sqlite3 database memory") - } else { + default: if !strings.HasSuffix(dbConf.Name, ".db") { dbConf.Name += ".db" } @@ -107,9 +114,10 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err } dialector = openSqlite(dsn) case conf.DatabaseTypePostgres: - if dbConf.CustomDSN != "" { + switch { + case dbConf.CustomDSN != "": dsn = dbConf.CustomDSN - } else if dbConf.Port == 0 { + case dbConf.Port == 0: dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=%s", dbConf.Host, dbConf.User, @@ -118,7 +126,7 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err dbConf.SslMode, ) log.Infof("postgres database: %s", dbConf.Host) - } else { + default: dsn = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", dbConf.Host, dbConf.Port, @@ -136,7 +144,7 @@ func createDialector(dbConf conf.DatabaseConfig) (dialector gorm.Dialector, err default: log.Fatalf("unknown database type: %s", dbConf.Type) } - return + return dialector, err } func newDBLogger() logger.Interface { diff --git a/internal/bootstrap/gin.go b/internal/bootstrap/gin.go index 9fe1ef34..8135245d 100644 --- a/internal/bootstrap/gin.go +++ b/internal/bootstrap/gin.go @@ -8,7 +8,7 @@ import ( "github.com/synctv-org/synctv/utils" ) -func InitGinMode(ctx context.Context) error { +func InitGinMode(_ context.Context) error { if flags.Global.Dev { gin.SetMode(gin.DebugMode) } else { diff --git a/internal/bootstrap/init.go b/internal/bootstrap/init.go index 3ae7f640..6a7d28dd 100644 --- a/internal/bootstrap/init.go +++ b/internal/bootstrap/init.go @@ -6,12 +6,6 @@ import ( type Conf func(*Bootstrap) -func WithContext(ctx context.Context) Conf { - return func(b *Bootstrap) { - b.ctx = ctx - } -} - func WithTask(f ...Func) Conf { return func(b *Bootstrap) { b.task = append(b.task, f...) @@ -19,7 +13,6 @@ func WithTask(f ...Func) Conf { } type Bootstrap struct { - ctx context.Context task []Func } @@ -38,9 +31,9 @@ func (b *Bootstrap) Add(f ...Func) *Bootstrap { return b } -func (b *Bootstrap) Run() error { +func (b *Bootstrap) Run(ctx context.Context) error { for _, f := range b.task { - if err := f(b.ctx); err != nil { + if err := f(ctx); err != nil { return err } } diff --git a/internal/bootstrap/log.go b/internal/bootstrap/log.go index 8518c819..920409a7 100644 --- a/internal/bootstrap/log.go +++ b/internal/bootstrap/log.go @@ -31,7 +31,7 @@ var logCallerIgnoreFuncs = map[string]struct{}{ "github.com/synctv-org/synctv/server/middlewares.logColor": {}, } -func InitLog(ctx context.Context) (err error) { +func InitLog(_ context.Context) (err error) { setLog(logrus.StandardLogger()) forceColor := utils.ForceColor() if conf.Conf.Log.Enable { @@ -63,7 +63,7 @@ func InitLog(ctx context.Context) (err error) { case "json": logrus.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: time.DateTime, - CallerPrettyfier: func(f *runtime.Frame) (function string, file string) { + CallerPrettyfier: func(f *runtime.Frame) (function, file string) { if _, ok := logCallerIgnoreFuncs[f.Function]; ok { return "", "" } @@ -83,7 +83,7 @@ func InitLog(ctx context.Context) (err error) { FullTimestamp: true, TimestampFormat: time.DateTime, QuoteEmptyFields: true, - CallerPrettyfier: func(f *runtime.Frame) (function string, file string) { + CallerPrettyfier: func(f *runtime.Frame) (function, file string) { if _, ok := logCallerIgnoreFuncs[f.Function]; ok { return "", "" } @@ -95,7 +95,7 @@ func InitLog(ctx context.Context) (err error) { return nil } -func InitStdLog(ctx context.Context) error { +func InitStdLog(_ context.Context) error { logrus.StandardLogger().SetOutput(os.Stdout) log.SetOutput(os.Stdout) setLog(logrus.StandardLogger()) diff --git a/internal/bootstrap/op.go b/internal/bootstrap/op.go index a588dfaa..c9165caa 100644 --- a/internal/bootstrap/op.go +++ b/internal/bootstrap/op.go @@ -6,6 +6,6 @@ import ( "github.com/synctv-org/synctv/internal/op" ) -func InitOp(ctx context.Context) error { +func InitOp(_ context.Context) error { return op.Init(4096) } diff --git a/internal/bootstrap/provider.go b/internal/bootstrap/provider.go index f016484a..db8e55fb 100644 --- a/internal/bootstrap/provider.go +++ b/internal/bootstrap/provider.go @@ -34,47 +34,53 @@ type ProviderGroupSetting struct { SignupNeedReview settings.BoolSetting } -var Oauth2EnabledCache = refreshcache0.NewRefreshCache[[]provider.OAuth2Provider](func(context.Context) ([]provider.OAuth2Provider, error) { - ps := providers.EnabledProvider() - r := make([]provider.OAuth2Provider, 0, ps.Len()) - ps.Range(func(p provider.OAuth2Provider, value struct{}) bool { - r = append(r, p) - return true - }) - slices.SortStableFunc(r, func(a, b provider.OAuth2Provider) int { - if a == b { - return 0 - } else if natural.Less(a, b) { - return -1 - } - return 1 - }) - return r, nil -}, 0) - -var Oauth2SignupEnabledCache = refreshcache0.NewRefreshCache[[]provider.OAuth2Provider](func(ctx context.Context) ([]provider.OAuth2Provider, error) { - ps := providers.EnabledProvider() - r := make([]provider.OAuth2Provider, 0, ps.Len()) - ps.Range(func(p provider.OAuth2Provider, value struct{}) bool { - group := fmt.Sprintf("%s_%s", model.SettingGroupOauth2, p) - groupSettings := ProviderGroupSettings[group] - if groupSettings.Enabled.Get() && !groupSettings.DisableUserSignup.Get() { +var Oauth2EnabledCache = refreshcache0.NewRefreshCache( + func(context.Context) ([]provider.OAuth2Provider, error) { + ps := providers.EnabledProvider() + r := make([]provider.OAuth2Provider, 0, ps.Len()) + ps.Range(func(p provider.OAuth2Provider, _ struct{}) bool { r = append(r, p) - } - return true - }) - slices.SortStableFunc(r, func(a, b provider.OAuth2Provider) int { - if a == b { - return 0 - } else if natural.Less(a, b) { - return -1 - } - return 1 - }) - return r, nil -}, 0) + return true + }) + slices.SortStableFunc(r, func(a, b provider.OAuth2Provider) int { + if a == b { + return 0 + } else if natural.Less(a, b) { + return -1 + } + return 1 + }) + return r, nil + }, + 0, +) + +var Oauth2SignupEnabledCache = refreshcache0.NewRefreshCache( + func(_ context.Context) ([]provider.OAuth2Provider, error) { + ps := providers.EnabledProvider() + r := make([]provider.OAuth2Provider, 0, ps.Len()) + ps.Range(func(p provider.OAuth2Provider, _ struct{}) bool { + group := fmt.Sprintf("%s_%s", model.SettingGroupOauth2, p) + groupSettings := ProviderGroupSettings[group] + if groupSettings.Enabled.Get() && !groupSettings.DisableUserSignup.Get() { + r = append(r, p) + } + return true + }) + slices.SortStableFunc(r, func(a, b provider.OAuth2Provider) int { + if a == b { + return 0 + } else if natural.Less(a, b) { + return -1 + } + return 1 + }) + return r, nil + }, + 0, +) -func InitProvider(ctx context.Context) (err error) { +func InitProvider(_ context.Context) (err error) { logOur := log.StandardLogger().Writer() logLevle := hclog.Info if flags.Global.Dev { @@ -115,7 +121,7 @@ func InitProviderSetting(pi provider.Provider) { ProviderGroupSettings[group] = groupSettings groupSettings.Enabled = settings.NewBoolSetting(group+"_enabled", false, group, - settings.WithBeforeInitBool(func(bs settings.BoolSetting, b bool) (bool, error) { + settings.WithBeforeInitBool(func(_ settings.BoolSetting, b bool) (bool, error) { defer func() { _, _ = Oauth2EnabledCache.Refresh(context.Background()) }() if b { return b, providers.EnableProvider(pi.Provider()) @@ -123,7 +129,7 @@ func InitProviderSetting(pi provider.Provider) { return b, providers.DisableProvider(pi.Provider()) }), settings.WithInitPriorityBool(1), - settings.WithBeforeSetBool(func(bs settings.BoolSetting, b bool) (bool, error) { + settings.WithBeforeSetBool(func(_ settings.BoolSetting, b bool) (bool, error) { defer func() { _, _ = Oauth2EnabledCache.Refresh(context.Background()) }() if b { return b, providers.EnableProvider(pi.Provider()) @@ -135,49 +141,65 @@ func InitProviderSetting(pi provider.Provider) { opt := provider.Oauth2Option{} groupSettings.ClientID = settings.NewStringSetting(group+"_client_id", opt.ClientID, group, - settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeInitString(func(_ settings.StringSetting, s string) (string, error) { opt.ClientID = s pi.Init(opt) return s, nil }), settings.WithInitPriorityString(1), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { opt.ClientID = s pi.Init(opt) return s, nil })) - groupSettings.ClientSecret = settings.NewStringSetting(group+"_client_secret", opt.ClientSecret, group, - settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { + groupSettings.ClientSecret = settings.NewStringSetting( + group+"_client_secret", + opt.ClientSecret, + group, + settings.WithBeforeInitString(func(_ settings.StringSetting, s string) (string, error) { opt.ClientSecret = s pi.Init(opt) return s, nil }), settings.WithInitPriorityString(1), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { opt.ClientSecret = s pi.Init(opt) return s, nil - })) + }), + ) - groupSettings.RedirectURL = settings.NewStringSetting(group+"_redirect_url", opt.RedirectURL, group, - settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { + groupSettings.RedirectURL = settings.NewStringSetting( + group+"_redirect_url", + opt.RedirectURL, + group, + settings.WithBeforeInitString(func(_ settings.StringSetting, s string) (string, error) { opt.RedirectURL = s pi.Init(opt) return s, nil }), settings.WithInitPriorityString(1), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { opt.RedirectURL = s pi.Init(opt) return s, nil - })) + }), + ) - groupSettings.DisableUserSignup = settings.NewBoolSetting(group+"_disable_user_signup", false, group) + groupSettings.DisableUserSignup = settings.NewBoolSetting( + group+"_disable_user_signup", + false, + group, + ) - groupSettings.SignupNeedReview = settings.NewBoolSetting(group+"_signup_need_review", false, group) + groupSettings.SignupNeedReview = settings.NewBoolSetting( + group+"_signup_need_review", + false, + group, + ) - if registerSetting, ok := pi.(provider.ProviderRegistSetting); ok { + if registerSetting, ok := pi.(provider.RegistSetting); ok { registerSetting.RegistSetting(group) } } @@ -188,7 +210,7 @@ func InitAggregationProviderSetting(pi provider.Provider) { ProviderGroupSettings[group] = groupSettings groupSettings.Enabled = settings.LoadOrNewBoolSetting(group+"_enabled", false, group, - settings.WithBeforeSetBool(func(bs settings.BoolSetting, b bool) (bool, error) { + settings.WithBeforeSetBool(func(_ settings.BoolSetting, b bool) (bool, error) { defer func() { _, _ = Oauth2EnabledCache.Refresh(context.Background()) }() if b { return b, providers.EnableProvider(pi.Provider()) @@ -199,35 +221,59 @@ func InitAggregationProviderSetting(pi provider.Provider) { opt := provider.Oauth2Option{} - groupSettings.ClientID = settings.LoadOrNewStringSetting(group+"_client_id", opt.ClientID, group) + groupSettings.ClientID = settings.LoadOrNewStringSetting( + group+"_client_id", + opt.ClientID, + group, + ) opt.ClientID = groupSettings.ClientID.Get() - groupSettings.ClientID.SetBeforeSet(func(ss settings.StringSetting, s string) (string, error) { + groupSettings.ClientID.SetBeforeSet(func(_ settings.StringSetting, s string) (string, error) { opt.ClientID = s pi.Init(opt) return s, nil }) - groupSettings.ClientSecret = settings.LoadOrNewStringSetting(group+"_client_secret", opt.ClientSecret, group) + groupSettings.ClientSecret = settings.LoadOrNewStringSetting( + group+"_client_secret", + opt.ClientSecret, + group, + ) opt.ClientSecret = groupSettings.ClientSecret.Get() - groupSettings.ClientSecret.SetBeforeSet(func(ss settings.StringSetting, s string) (string, error) { - opt.ClientSecret = s - pi.Init(opt) - return s, nil - }) + groupSettings.ClientSecret.SetBeforeSet( + func(_ settings.StringSetting, s string) (string, error) { + opt.ClientSecret = s + pi.Init(opt) + return s, nil + }, + ) - groupSettings.RedirectURL = settings.LoadOrNewStringSetting(group+"_redirect_url", opt.RedirectURL, group) + groupSettings.RedirectURL = settings.LoadOrNewStringSetting( + group+"_redirect_url", + opt.RedirectURL, + group, + ) opt.RedirectURL = groupSettings.RedirectURL.Get() - groupSettings.RedirectURL.SetBeforeSet(func(ss settings.StringSetting, s string) (string, error) { - opt.RedirectURL = s - pi.Init(opt) - return s, nil - }) + groupSettings.RedirectURL.SetBeforeSet( + func(_ settings.StringSetting, s string) (string, error) { + opt.RedirectURL = s + pi.Init(opt) + return s, nil + }, + ) pi.Init(opt) - groupSettings.DisableUserSignup = settings.LoadOrNewBoolSetting(group+"_disable_user_signup", false, group) + groupSettings.DisableUserSignup = settings.LoadOrNewBoolSetting( + group+"_disable_user_signup", + false, + group, + ) - groupSettings.SignupNeedReview = settings.LoadOrNewBoolSetting(group+"_signup_need_review", false, group) + groupSettings.SignupNeedReview = settings.LoadOrNewBoolSetting( + group+"_signup_need_review", + false, + group, + ) } func InitAggregationSetting(pi provider.AggregationProviderInterface) { @@ -236,25 +282,26 @@ func InitAggregationSetting(pi provider.AggregationProviderInterface) { switch pi := pi.(type) { case *aggregations.Rainbow: settings.NewStringSetting(group+"_api", aggregations.DefaultRainbowAPI, group, - settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeInitString(func(_ settings.StringSetting, s string) (string, error) { pi.SetAPI(s) return s, nil }, ), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { pi.SetAPI(s) return s, nil }, ), ) + default: } list := settings.NewStringSetting(group+"_enabled_list", "", group, - settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeInitString(func(_ settings.StringSetting, s string) (string, error) { return s, nil }), settings.WithInitPriorityString(1), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { if s == "" { return s, nil } @@ -269,11 +316,14 @@ func InitAggregationSetting(pi provider.AggregationProviderInterface) { ) settings.NewBoolSetting(group+"_enabled", false, group, - settings.WithBeforeInitBool(func(bs settings.BoolSetting, b bool) (bool, error) { + settings.WithBeforeInitBool(func(_ settings.BoolSetting, b bool) (bool, error) { if b { s := list.Get() if s == "" { - log.Warnf("aggregation provider %s enabled, but no provider enabled", pi.Provider()) + log.Warnf( + "aggregation provider %s enabled, but no provider enabled", + pi.Provider(), + ) } all := pi.Providers() list := strings.Split(s, ",") @@ -288,7 +338,11 @@ func InitAggregationSetting(pi provider.AggregationProviderInterface) { pi2, err := provider.ExtractProviders(pi, enabled...) if err != nil { - log.Errorf("aggregation provider %s enabled, but extract provider failed: %s", pi.Provider(), err) + log.Errorf( + "aggregation provider %s enabled, but extract provider failed: %s", + pi.Provider(), + err, + ) return b, nil } for _, pi2 := range pi2 { @@ -298,7 +352,7 @@ func InitAggregationSetting(pi provider.AggregationProviderInterface) { } return b, nil }), - settings.WithBeforeSetBool(func(bs settings.BoolSetting, b bool) (bool, error) { + settings.WithBeforeSetBool(func(_ settings.BoolSetting, b bool) (bool, error) { if len(list.Get()) == 0 { return b, errors.New("enabled provider list is empty") } diff --git a/internal/bootstrap/rtmp.go b/internal/bootstrap/rtmp.go index 12681ed4..4e15f8c1 100644 --- a/internal/bootstrap/rtmp.go +++ b/internal/bootstrap/rtmp.go @@ -12,7 +12,7 @@ import ( rtmps "github.com/zijiren233/livelib/server" ) -func InitRtmp(ctx context.Context) error { +func InitRtmp(_ context.Context) error { s := rtmps.NewRtmpServer(auth) rtmp.Init(s) return nil diff --git a/internal/bootstrap/setting.go b/internal/bootstrap/setting.go index d097fd2c..15a99a37 100644 --- a/internal/bootstrap/setting.go +++ b/internal/bootstrap/setting.go @@ -8,7 +8,7 @@ import ( "github.com/synctv-org/synctv/internal/settings" ) -func InitSetting(ctx context.Context) error { +func InitSetting(_ context.Context) error { return initAndFixSettings() } diff --git a/internal/bootstrap/sysNotify.go b/internal/bootstrap/sysNotify.go index 91b54b42..c67185b5 100644 --- a/internal/bootstrap/sysNotify.go +++ b/internal/bootstrap/sysNotify.go @@ -6,7 +6,7 @@ import ( sysnotify "github.com/synctv-org/synctv/internal/sysnotify" ) -func InitSysNotify(ctx context.Context) error { +func InitSysNotify(_ context.Context) error { sysnotify.Init() return nil } diff --git a/internal/bootstrap/update.go b/internal/bootstrap/update.go index cc05f285..2b2ffb62 100644 --- a/internal/bootstrap/update.go +++ b/internal/bootstrap/update.go @@ -69,7 +69,7 @@ func InitCheckUpdate(ctx context.Context) error { return nil } -func check(ctx context.Context, v *version.Info) (need bool, latest string, url string, err error) { +func check(ctx context.Context, v *version.Info) (need bool, latest, url string, err error) { l, err := v.CheckLatest(ctx) if err != nil { return false, "", "", err diff --git a/internal/cache/alist.go b/internal/cache/alist.go index cc7602ef..80ff3f55 100644 --- a/internal/cache/alist.go +++ b/internal/cache/alist.go @@ -31,12 +31,18 @@ type AlistUserCacheData struct { } func NewAlistUserCache(userID string) *AlistUserCache { - return newMapCache[*AlistUserCacheData, struct{}](func(ctx context.Context, key string, args ...struct{}) (*AlistUserCacheData, error) { - return AlistAuthorizationCacheWithUserIDInitFunc(ctx, userID, key) - }, -1) + return newMapCache( + func(ctx context.Context, key string, _ ...struct{}) (*AlistUserCacheData, error) { + return AlistAuthorizationCacheWithUserIDInitFunc(ctx, userID, key) + }, + -1, + ) } -func AlistAuthorizationCacheWithUserIDInitFunc(ctx context.Context, userID, serverID string) (*AlistUserCacheData, error) { +func AlistAuthorizationCacheWithUserIDInitFunc( + ctx context.Context, + userID, serverID string, +) (*AlistUserCacheData, error) { v, err := db.GetAlistVendor(userID, serverID) if err != nil { return nil, err @@ -44,7 +50,10 @@ func AlistAuthorizationCacheWithUserIDInitFunc(ctx context.Context, userID, serv return AlistAuthorizationCacheWithConfigInitFunc(ctx, v) } -func AlistAuthorizationCacheWithConfigInitFunc(ctx context.Context, v *model.AlistVendor) (*AlistUserCacheData, error) { +func AlistAuthorizationCacheWithConfigInitFunc( + ctx context.Context, + v *model.AlistVendor, +) (*AlistUserCacheData, error) { cli := vendor.LoadAlistClient(v.Backend) model.GenAlistServerID(v) @@ -74,7 +83,7 @@ func AlistAuthorizationCacheWithConfigInitFunc(ctx context.Context, v *model.Ali return &AlistUserCacheData{ Host: v.Host, ServerID: v.ServerID, - Token: resp.Token, + Token: resp.GetToken(), Backend: v.Backend, }, nil } @@ -116,13 +125,15 @@ type SubtitleDataCache = refreshcache0.RefreshCache[[]byte] const subtitleMaxLength = 15 * 1024 * 1024 -func newAliSubtitles(list []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscodingSubtitleTaskList) []*AlistSubtitle { +func newAliSubtitles( + list []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscodingSubtitleTaskList, +) []*AlistSubtitle { caches := make([]*AlistSubtitle, len(list)) for i, v := range list { - if v.Status != "finished" { + if v.GetStatus() != "finished" { return nil } - url := v.Url + url := v.GetUrl() caches[i] = &AlistSubtitle{ Cache: refreshcache0.NewRefreshCache(func(ctx context.Context) ([]byte, error) { r, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) @@ -138,28 +149,41 @@ func newAliSubtitles(list []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscod return nil, fmt.Errorf("status code: %d", resp.StatusCode) } if resp.ContentLength > subtitleMaxLength { - return nil, fmt.Errorf("subtitle too large, got: %d, max: %d", resp.ContentLength, subtitleMaxLength) + return nil, fmt.Errorf( + "subtitle too large, got: %d, max: %d", + resp.ContentLength, + subtitleMaxLength, + ) } return io.ReadAll(io.LimitReader(resp.Body, subtitleMaxLength)) }, -1), - Name: v.Language, - URL: v.Url, - Type: utils.GetFileExtension(v.Url), + Name: v.GetLanguage(), + URL: v.GetUrl(), + Type: utils.GetFileExtension(v.GetUrl()), } } return caches } -func genAliM3U8ListFile(urls []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscodingTaskList) []byte { +func genAliM3U8ListFile( + urls []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscodingTaskList, +) []byte { buf := bytes.NewBuffer(nil) buf.WriteString("#EXTM3U\n") buf.WriteString("#EXT-X-VERSION:3\n") for _, v := range urls { - if v.Status != "finished" { + if v.GetStatus() != "finished" { return nil } - buf.WriteString(fmt.Sprintf("#EXT-X-STREAM-INF:BANDWIDTH=%d,RESOLUTION=%dx%d,NAME=\"%d\"\n", v.TemplateWidth*v.TemplateHeight, v.TemplateWidth, v.TemplateHeight, v.TemplateWidth)) - buf.WriteString(v.Url + "\n") + fmt.Fprintf( + buf, + "#EXT-X-STREAM-INF:BANDWIDTH=%d,RESOLUTION=%dx%d,NAME=\"%d\"\n", + v.GetTemplateWidth()*v.GetTemplateHeight(), + v.GetTemplateWidth(), + v.GetTemplateHeight(), + v.GetTemplateWidth(), + ) + buf.WriteString(v.GetUrl() + "\n") } return buf.Bytes() } @@ -169,7 +193,10 @@ type AlistMovieCacheFuncArgs struct { UserAgent string } -func NewAlistMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx context.Context, args *AlistMovieCacheFuncArgs) (*AlistMovieCacheData, error) { +func NewAlistMovieCacheInitFunc( + movie *model.Movie, + subPath string, +) func(ctx context.Context, args *AlistMovieCacheFuncArgs) (*AlistMovieCacheData, error) { return func(ctx context.Context, args *AlistMovieCacheFuncArgs) (*AlistMovieCacheData, error) { if err := validateArgs(args, movie, subPath); err != nil { return nil, err @@ -188,27 +215,42 @@ func NewAlistMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx con return nil, errors.New("not bind alist vendor") } - cli := vendor.LoadAlistClient(movie.MovieBase.VendorInfo.Backend) - fg, err := getFsGet(ctx, cli, aucd, truePath, movie.MovieBase.VendorInfo.Alist.Password, args.UserAgent) + cli := vendor.LoadAlistClient(movie.VendorInfo.Backend) + fg, err := getFsGet( + ctx, + cli, + aucd, + truePath, + movie.VendorInfo.Alist.Password, + args.UserAgent, + ) if err != nil { return nil, err } - if fg.IsDir { + if fg.GetIsDir() { return nil, fmt.Errorf("path is dir: %s", truePath) } cache := &AlistMovieCacheData{ - URL: fg.RawUrl, - Provider: fg.Provider, + URL: fg.GetRawUrl(), + Provider: fg.GetProvider(), } - if err := processSubtitles(ctx, cli, aucd, fg, truePath, movie.MovieBase.VendorInfo.Alist.Password, args.UserAgent, cache); err != nil { + if err := processSubtitles(ctx, cli, aucd, fg, truePath, movie.VendorInfo.Alist.Password, args.UserAgent, cache); err != nil { return nil, err } - if fg.Provider == AlistProviderAli { - processAliProvider(ctx, fg.RawUrl, cli, aucd, truePath, movie.MovieBase.VendorInfo.Alist.Password, cache) + if fg.GetProvider() == AlistProviderAli { + processAliProvider( + ctx, + fg.GetRawUrl(), + cli, + aucd, + truePath, + movie.VendorInfo.Alist.Password, + cache, + ) } return cache, nil @@ -229,7 +271,7 @@ func validateArgs(args *AlistMovieCacheFuncArgs, movie *model.Movie, subPath str } func getServerIDAndPath(movie *model.Movie, subPath string) (string, string, error) { - serverID, truePath, err := movie.MovieBase.VendorInfo.Alist.ServerIDAndFilePath() + serverID, truePath, err := movie.VendorInfo.Alist.ServerIDAndFilePath() if err != nil { return "", "", err } @@ -245,7 +287,12 @@ func getServerIDAndPath(movie *model.Movie, subPath string) (string, string, err return serverID, truePath, nil } -func getFsGet(ctx context.Context, cli alist.AlistHTTPServer, aucd *AlistUserCacheData, truePath, password, userAgent string) (*alist.FsGetResp, error) { +func getFsGet( + ctx context.Context, + cli alist.AlistHTTPServer, + aucd *AlistUserCacheData, + truePath, password, userAgent string, +) (*alist.FsGetResp, error) { return cli.FsGet(ctx, &alist.FsGetReq{ Host: aucd.Host, Token: aucd.Token, @@ -255,27 +302,34 @@ func getFsGet(ctx context.Context, cli alist.AlistHTTPServer, aucd *AlistUserCac }) } -func processSubtitles(ctx context.Context, cli alist.AlistHTTPServer, aucd *AlistUserCacheData, fg *alist.FsGetResp, truePath, password, userAgent string, cache *AlistMovieCacheData) error { - prefix := strings.TrimSuffix(truePath, fg.Name) - for _, related := range fg.Related { - if related.Type != 4 { +func processSubtitles( + ctx context.Context, + cli alist.AlistHTTPServer, + aucd *AlistUserCacheData, + fg *alist.FsGetResp, + truePath, password, userAgent string, + cache *AlistMovieCacheData, +) error { + prefix := strings.TrimSuffix(truePath, fg.GetName()) + for _, related := range fg.GetRelated() { + if related.GetType() != 4 { continue } - if utils.GetFileExtension(related.Name) == "xml" { + if utils.GetFileExtension(related.GetName()) == "xml" { continue } - resp, err := getFsGet(ctx, cli, aucd, prefix+related.Name, password, userAgent) + resp, err := getFsGet(ctx, cli, aucd, prefix+related.GetName(), password, userAgent) if err != nil { return err } subtitle := &AlistSubtitle{ - Name: related.Name, - URL: resp.RawUrl, - Type: utils.GetFileExtension(resp.Name), + Name: related.GetName(), + URL: resp.GetRawUrl(), + Type: utils.GetFileExtension(resp.GetName()), Cache: refreshcache0.NewRefreshCache(func(ctx context.Context) ([]byte, error) { - return fetchSubtitleContent(ctx, resp.RawUrl) + return fetchSubtitleContent(ctx, resp.GetRawUrl()) }, -1), } cache.Subtitles = append(cache.Subtitles, subtitle) @@ -297,12 +351,23 @@ func fetchSubtitleContent(ctx context.Context, url string) ([]byte, error) { return nil, fmt.Errorf("status code: %d", resp.StatusCode) } if resp.ContentLength > subtitleMaxLength { - return nil, fmt.Errorf("subtitle too large, got: %d, max: %d", resp.ContentLength, subtitleMaxLength) + return nil, fmt.Errorf( + "subtitle too large, got: %d, max: %d", + resp.ContentLength, + subtitleMaxLength, + ) } return io.ReadAll(io.LimitReader(resp.Body, subtitleMaxLength)) } -func processAliProvider(_ context.Context, firstURL string, cli alist.AlistHTTPServer, aucd *AlistUserCacheData, truePath, password string, cache *AlistMovieCacheData) { +func processAliProvider( + _ context.Context, + firstURL string, + cli alist.AlistHTTPServer, + aucd *AlistUserCacheData, + truePath, password string, + cache *AlistMovieCacheData, +) { cache.Ali = refreshcache0.NewRefreshCache(func(ctx context.Context) (*AlistAliCache, error) { var url string if firstURL != "" { @@ -318,7 +383,7 @@ func processAliProvider(_ context.Context, firstURL string, cli alist.AlistHTTPS if err != nil { return nil, err } - url = u.RawUrl + url = u.GetRawUrl() } fo, err := cli.FsOther(ctx, &alist.FsOtherReq{ Host: aucd.Host, @@ -331,9 +396,13 @@ func processAliProvider(_ context.Context, firstURL string, cli alist.AlistHTTPS return nil, err } return &AlistAliCache{ - URL: url, - M3U8ListFile: genAliM3U8ListFile(fo.VideoPreviewPlayInfo.LiveTranscodingTaskList), - Subtitles: newAliSubtitles(fo.VideoPreviewPlayInfo.LiveTranscodingSubtitleTaskList), + URL: url, + M3U8ListFile: genAliM3U8ListFile( + fo.GetVideoPreviewPlayInfo().GetLiveTranscodingTaskList(), + ), + Subtitles: newAliSubtitles( + fo.GetVideoPreviewPlayInfo().GetLiveTranscodingSubtitleTaskList(), + ), }, nil }, 14*time.Minute) } diff --git a/internal/cache/bilibili.go b/internal/cache/bilibili.go index 7f5e7b31..ebc98631 100644 --- a/internal/cache/bilibili.go +++ b/internal/cache/bilibili.go @@ -37,13 +37,19 @@ type BilibiliSubtitleCacheItem struct { URL string } -func NewBilibiliSharedMpdCacheInitFunc(movie *model.Movie) func(ctx context.Context, args *BilibiliUserCache) (*BilibiliMpdCache, error) { +func NewBilibiliSharedMpdCacheInitFunc( + movie *model.Movie, +) func(ctx context.Context, args *BilibiliUserCache) (*BilibiliMpdCache, error) { return func(ctx context.Context, args *BilibiliUserCache) (*BilibiliMpdCache, error) { return BilibiliSharedMpdCacheInitFunc(ctx, movie, args) } } -func BilibiliSharedMpdCacheInitFunc(ctx context.Context, movie *model.Movie, args *BilibiliUserCache) (*BilibiliMpdCache, error) { +func BilibiliSharedMpdCacheInitFunc( + ctx context.Context, + movie *model.Movie, + args *BilibiliUserCache, +) (*BilibiliMpdCache, error) { if args == nil { return nil, errors.New("no bilibili user cache data") } @@ -53,8 +59,8 @@ func BilibiliSharedMpdCacheInitFunc(ctx context.Context, movie *model.Movie, arg return nil, err } - cli := vendor.LoadBilibiliClient(movie.MovieBase.VendorInfo.Backend) - m, hevcM, err := getBilibiliMpd(ctx, cli, movie.MovieBase.VendorInfo.Bilibili, cookies) + cli := vendor.LoadBilibiliClient(movie.VendorInfo.Backend) + m, hevcM, err := getBilibiliMpd(ctx, cli, movie.VendorInfo.Bilibili, cookies) if err != nil { return nil, err } @@ -80,7 +86,12 @@ func getBilibiliCookies(ctx context.Context, args *BilibiliUserCache) ([]*http.C return vendorInfo.Cookies, nil } -func getBilibiliMpd(ctx context.Context, cli bilibili.BilibiliHTTPServer, biliInfo *model.BilibiliStreamingInfo, cookies []*http.Cookie) (*mpd.MPD, *mpd.MPD, error) { +func getBilibiliMpd( + ctx context.Context, + cli bilibili.BilibiliHTTPServer, + biliInfo *model.BilibiliStreamingInfo, + cookies []*http.Cookie, +) (*mpd.MPD, *mpd.MPD, error) { cookiesMap := utils.HTTPCookieToMap(cookies) switch { @@ -92,7 +103,7 @@ func getBilibiliMpd(ctx context.Context, cli bilibili.BilibiliHTTPServer, biliIn if err != nil { return nil, nil, err } - return parseMpdResponse(resp.Mpd, resp.HevcMpd) + return parseMpdResponse(resp.GetMpd(), resp.GetHevcMpd()) case biliInfo.Bvid != "": resp, err := cli.GetDashVideoURL(ctx, &bilibili.GetDashVideoURLReq{ @@ -103,7 +114,7 @@ func getBilibiliMpd(ctx context.Context, cli bilibili.BilibiliHTTPServer, biliIn if err != nil { return nil, nil, err } - return parseMpdResponse(resp.Mpd, resp.HevcMpd) + return parseMpdResponse(resp.GetMpd(), resp.GetHevcMpd()) default: return nil, nil, errors.New("bvid and epid are empty") @@ -192,13 +203,19 @@ func BilibiliMpdToString(mpdRaw *mpd.MPD, token string) (string, error) { return newMpdRaw.WriteToString() } -func NewBilibiliNoSharedMovieCacheInitFunc(movie *model.Movie) func(ctx context.Context, key string, args ...*BilibiliUserCache) (string, error) { - return func(ctx context.Context, key string, args ...*BilibiliUserCache) (string, error) { +func NewBilibiliNoSharedMovieCacheInitFunc( + movie *model.Movie, +) func(ctx context.Context, _ string, args ...*BilibiliUserCache) (string, error) { + return func(ctx context.Context, _ string, args ...*BilibiliUserCache) (string, error) { return BilibiliNoSharedMovieCacheInitFunc(ctx, movie, args...) } } -func BilibiliNoSharedMovieCacheInitFunc(ctx context.Context, movie *model.Movie, args ...*BilibiliUserCache) (string, error) { +func BilibiliNoSharedMovieCacheInitFunc( + ctx context.Context, + movie *model.Movie, + args ...*BilibiliUserCache, +) (string, error) { if len(args) == 0 { return "", errors.New("no bilibili user cache data") } @@ -211,9 +228,9 @@ func BilibiliNoSharedMovieCacheInitFunc(ctx context.Context, movie *model.Movie, } else { cookies = vendorInfo.Cookies } - cli := vendor.LoadBilibiliClient(movie.MovieBase.VendorInfo.Backend) + cli := vendor.LoadBilibiliClient(movie.VendorInfo.Backend) var u string - biliInfo := movie.MovieBase.VendorInfo.Bilibili + biliInfo := movie.VendorInfo.Bilibili switch { case biliInfo.Epid != 0: resp, err := cli.GetPGCURL(ctx, &bilibili.GetPGCURLReq{ @@ -223,7 +240,7 @@ func BilibiliNoSharedMovieCacheInitFunc(ctx context.Context, movie *model.Movie, if err != nil { return "", err } - u = resp.Url + u = resp.GetUrl() case biliInfo.Bvid != "": resp, err := cli.GetVideoURL(ctx, &bilibili.GetVideoURLReq{ @@ -234,7 +251,7 @@ func BilibiliNoSharedMovieCacheInitFunc(ctx context.Context, movie *model.Movie, if err != nil { return "", err } - u = resp.Url + u = resp.GetUrl() default: return "", errors.New("bvid and epid are empty") @@ -262,18 +279,24 @@ type bilibiliSubtitleResp struct { BackgroundAlpha float64 `json:"background_alpha"` } -func NewBilibiliSubtitleCacheInitFunc(movie *model.Movie) func(ctx context.Context, args *BilibiliUserCache) (BilibiliSubtitleCache, error) { +func NewBilibiliSubtitleCacheInitFunc( + movie *model.Movie, +) func(ctx context.Context, args *BilibiliUserCache) (BilibiliSubtitleCache, error) { return func(ctx context.Context, args *BilibiliUserCache) (BilibiliSubtitleCache, error) { return BilibiliSubtitleCacheInitFunc(ctx, movie, args) } } -func BilibiliSubtitleCacheInitFunc(ctx context.Context, movie *model.Movie, args *BilibiliUserCache) (BilibiliSubtitleCache, error) { +func BilibiliSubtitleCacheInitFunc( + ctx context.Context, + movie *model.Movie, + args *BilibiliUserCache, +) (BilibiliSubtitleCache, error) { if args == nil { return nil, errors.New("no bilibili user cache data") } - biliInfo := movie.MovieBase.VendorInfo.Bilibili + biliInfo := movie.VendorInfo.Bilibili if biliInfo.Bvid == "" || biliInfo.Cid == 0 { return nil, errors.New("bvid or cid is empty") } @@ -289,7 +312,7 @@ func BilibiliSubtitleCacheInitFunc(ctx context.Context, movie *model.Movie, args } cookies = vendorInfo.Cookies - cli := vendor.LoadBilibiliClient(movie.MovieBase.VendorInfo.Backend) + cli := vendor.LoadBilibiliClient(movie.VendorInfo.Backend) resp, err := cli.GetSubtitles(ctx, &bilibili.GetSubtitlesReq{ Cookies: utils.HTTPCookieToMap(cookies), Bvid: biliInfo.Bvid, @@ -298,11 +321,11 @@ func BilibiliSubtitleCacheInitFunc(ctx context.Context, movie *model.Movie, args if err != nil { return nil, err } - subtitleCache := make(BilibiliSubtitleCache, len(resp.Subtitles)) - for k, v := range resp.Subtitles { + subtitleCache := make(BilibiliSubtitleCache, len(resp.GetSubtitles())) + for k, v := range resp.GetSubtitles() { subtitleCache[k] = &BilibiliSubtitleCacheItem{ URL: v, - Srt: refreshcache0.NewRefreshCache[[]byte](func(ctx context.Context) ([]byte, error) { + Srt: refreshcache0.NewRefreshCache(func(ctx context.Context) ([]byte, error) { return translateBilibiliSubtitleToSrt(ctx, v) }, 0), } @@ -315,12 +338,12 @@ func convertToSRT(subtitles *bilibiliSubtitleResp) []byte { srt := bytes.NewBuffer(nil) counter := 0 for _, subtitle := range subtitles.Body { - srt.WriteString( - fmt.Sprintf("%d\n%s --> %s\n%s\n\n", - counter, - formatTime(subtitle.From), - formatTime(subtitle.To), - subtitle.Content)) + fmt.Fprintf(srt, + "%d\n%s --> %s\n%s\n\n", + counter, + formatTime(subtitle.From), + formatTime(subtitle.To), + subtitle.Content) counter++ } return srt.Bytes() @@ -366,25 +389,30 @@ func genBilibiliLiveM3U8ListFile(urls []*bilibili.LiveStream) []byte { buf.WriteString("#EXTM3U\n") buf.WriteString("#EXT-X-VERSION:3\n") for _, v := range urls { - if len(v.Urls) == 0 { + if len(v.GetUrls()) == 0 { continue } - buf.WriteString(fmt.Sprintf("#EXT-X-STREAM-INF:BANDWIDTH=%d,NAME=\"%s\"\n", 1920*1080*v.Quality, v.Desc)) - buf.WriteString(v.Urls[0] + "\n") + fmt.Fprintf( + buf, + "#EXT-X-STREAM-INF:BANDWIDTH=%d,NAME=\"%s\"\n", + 1920*1080*v.GetQuality(), + v.GetDesc(), + ) + buf.WriteString(v.GetUrls()[0] + "\n") } return buf.Bytes() } func BilibiliLiveCacheInitFunc(ctx context.Context, movie *model.Movie) ([]byte, error) { - cli := vendor.LoadBilibiliClient(movie.MovieBase.VendorInfo.Backend) + cli := vendor.LoadBilibiliClient(movie.VendorInfo.Backend) resp, err := cli.GetLiveStreams(ctx, &bilibili.GetLiveStreamsReq{ - Cid: movie.MovieBase.VendorInfo.Bilibili.Cid, + Cid: movie.VendorInfo.Bilibili.Cid, Hls: true, }) if err != nil { return nil, err } - return genBilibiliLiveM3U8ListFile(resp.LiveStreams), nil + return genBilibiliLiveM3U8ListFile(resp.GetLiveStreams()), nil } func NewBilibiliDanmuCacheInitFunc(movie *model.Movie) func(ctx context.Context) ([]byte, error) { @@ -427,10 +455,16 @@ type BilibiliMovieCache struct { func NewBilibiliMovieCache(movie *model.Movie) *BilibiliMovieCache { return &BilibiliMovieCache{ NoSharedMovie: newMapCache(NewBilibiliNoSharedMovieCacheInitFunc(movie), time.Minute*55), - SharedMpd: refreshcache1.NewRefreshCache(NewBilibiliSharedMpdCacheInitFunc(movie), time.Minute*55), - Subtitle: refreshcache1.NewRefreshCache(NewBilibiliSubtitleCacheInitFunc(movie), -1), - Live: refreshcache0.NewRefreshCache(NewBilibiliLiveCacheInitFunc(movie), time.Minute*55), - Danmu: refreshcache0.NewRefreshCache(NewBilibiliDanmuCacheInitFunc(movie), -1), + SharedMpd: refreshcache1.NewRefreshCache( + NewBilibiliSharedMpdCacheInitFunc(movie), + time.Minute*55, + ), + Subtitle: refreshcache1.NewRefreshCache(NewBilibiliSubtitleCacheInitFunc(movie), -1), + Live: refreshcache0.NewRefreshCache( + NewBilibiliLiveCacheInitFunc(movie), + time.Minute*55, + ), + Danmu: refreshcache0.NewRefreshCache(NewBilibiliDanmuCacheInitFunc(movie), -1), } } @@ -443,13 +477,18 @@ type BilibiliUserCacheData struct { func NewBilibiliUserCache(userID string) *BilibiliUserCache { f := BilibiliAuthorizationCacheWithUserIDInitFunc(userID) - return refreshcache.NewRefreshCache(func(ctx context.Context, args ...struct{}) (*BilibiliUserCacheData, error) { - return f(ctx) - }, -1) + return refreshcache.NewRefreshCache( + func(ctx context.Context, _ ...struct{}) (*BilibiliUserCacheData, error) { + return f(ctx) + }, + -1, + ) } -func BilibiliAuthorizationCacheWithUserIDInitFunc(userID string) func(ctx context.Context, args ...struct{}) (*BilibiliUserCacheData, error) { - return func(ctx context.Context, args ...struct{}) (*BilibiliUserCacheData, error) { +func BilibiliAuthorizationCacheWithUserIDInitFunc( + userID string, +) func(ctx context.Context, _ ...struct{}) (*BilibiliUserCacheData, error) { + return func(_ context.Context, _ ...struct{}) (*BilibiliUserCacheData, error) { v, err := db.GetBilibiliVendor(userID) if err != nil { return nil, err diff --git a/internal/cache/cache.go b/internal/cache/cache.go index ebb661fe..c0b08b2b 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -6,7 +6,6 @@ import ( "time" "github.com/zijiren233/gencontainer/refreshcache" - "golang.org/x/exp/maps" ) type MapRefreshFunc[T any, A any] func(ctx context.Context, key string, args ...A) (T, error) @@ -18,7 +17,7 @@ type MapCache[T any, A any] struct { lock sync.RWMutex } -func newMapCache[T any, A any](refreshFunc MapRefreshFunc[T, A], maxAge time.Duration) *MapCache[T, A] { +func newMapCache[T, A any](refreshFunc MapRefreshFunc[T, A], maxAge time.Duration) *MapCache[T, A] { return &MapCache[T, A]{ cache: make(map[string]*refreshcache.RefreshCache[T, A]), refreshFunc: refreshFunc, @@ -33,7 +32,7 @@ func (b *MapCache[T, A]) Clear() { } func (b *MapCache[T, A]) clear() { - maps.Clear(b.cache) + clear(b.cache) } func (b *MapCache[T, A]) Delete(key string) { @@ -56,9 +55,12 @@ func (b *MapCache[T, A]) LoadOrStore(ctx context.Context, key string, args ...A) b.lock.Unlock() return c.Get(ctx, args...) } - c = refreshcache.NewRefreshCache[T, A](refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return b.refreshFunc(ctx, key, args...) - }), b.maxAge) + c = refreshcache.NewRefreshCache( + refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return b.refreshFunc(ctx, key, args...) + }), + b.maxAge, + ) b.cache[key] = c b.lock.Unlock() return c.Get(ctx, args...) @@ -78,9 +80,12 @@ func (b *MapCache[T, A]) StoreOrRefresh(ctx context.Context, key string, args .. b.lock.Unlock() return c.Refresh(ctx, args...) } - c = refreshcache.NewRefreshCache[T, A](refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return b.refreshFunc(ctx, key, args...) - }), b.maxAge) + c = refreshcache.NewRefreshCache( + refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return b.refreshFunc(ctx, key, args...) + }), + b.maxAge, + ) b.cache[key] = c b.lock.Unlock() return c.Refresh(ctx, args...) @@ -107,66 +112,91 @@ func (b *MapCache[T, A]) LoadOrNewCache(key string) *refreshcache.RefreshCache[T b.lock.Unlock() return c } - c = refreshcache.NewRefreshCache[T, A](refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return b.refreshFunc(ctx, key, args...) - }), b.maxAge) + c = refreshcache.NewRefreshCache( + refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return b.refreshFunc(ctx, key, args...) + }), + b.maxAge, + ) b.cache[key] = c b.lock.Unlock() return c } -func (b *MapCache[T, A]) LoadOrStoreWithDynamicFunc(ctx context.Context, key string, refreshFunc MapRefreshFunc[T, A], args ...A) (T, error) { +func (b *MapCache[T, A]) LoadOrStoreWithDynamicFunc( + ctx context.Context, + key string, + refreshFunc MapRefreshFunc[T, A], + args ...A, +) (T, error) { b.lock.RLock() c, loaded := b.cache[key] if loaded { b.lock.RUnlock() - return c.Data().Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } b.lock.RUnlock() b.lock.Lock() c, loaded = b.cache[key] if loaded { b.lock.Unlock() - return c.Data().Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } - c = refreshcache.NewRefreshCache[T, A](refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return b.refreshFunc(ctx, key, args...) - }), b.maxAge) + c = refreshcache.NewRefreshCache( + refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return b.refreshFunc(ctx, key, args...) + }), + b.maxAge, + ) b.cache[key] = c b.lock.Unlock() - return c.Data().Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Get(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } -func (b *MapCache[T, A]) StoreOrRefreshWithDynamicFunc(ctx context.Context, key string, refreshFunc MapRefreshFunc[T, A], args ...A) (T, error) { +func (b *MapCache[T, A]) StoreOrRefreshWithDynamicFunc( + ctx context.Context, + key string, + refreshFunc MapRefreshFunc[T, A], + args ...A, +) (T, error) { b.lock.RLock() c, ok := b.cache[key] if ok { b.lock.RUnlock() - return c.Data().Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } b.lock.RUnlock() b.lock.Lock() c, ok = b.cache[key] if ok { b.lock.Unlock() - return c.Data().Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } - c = refreshcache.NewRefreshCache[T, A](refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return b.refreshFunc(ctx, key, args...) - }), b.maxAge) + c = refreshcache.NewRefreshCache( + refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return b.refreshFunc(ctx, key, args...) + }), + b.maxAge, + ) b.cache[key] = c b.lock.Unlock() - return c.Data().Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { - return refreshFunc(ctx, key, args...) - }), args...) + return c.Data(). + Refresh(ctx, refreshcache.RefreshFunc[T, A](func(ctx context.Context, args ...A) (T, error) { + return refreshFunc(ctx, key, args...) + }), args...) } diff --git a/internal/cache/cache0.go b/internal/cache/cache0.go index 8b4a659b..05318e6c 100644 --- a/internal/cache/cache0.go +++ b/internal/cache/cache0.go @@ -6,7 +6,6 @@ import ( "time" "github.com/zijiren233/gencontainer/refreshcache0" - "golang.org/x/exp/maps" ) type MapRefreshFunc0[T any] func(ctx context.Context, key string) (T, error) @@ -33,7 +32,7 @@ func (b *MapCache0[T]) Clear() { } func (b *MapCache0[T]) clear() { - maps.Clear(b.cache) + clear(b.cache) } func (b *MapCache0[T]) Delete(key string) { @@ -56,7 +55,7 @@ func (b *MapCache0[T]) LoadOrStore(ctx context.Context, key string) (T, error) { b.lock.Unlock() return c.Get(ctx) } - c = refreshcache0.NewRefreshCache[T](func(ctx context.Context) (T, error) { + c = refreshcache0.NewRefreshCache(func(ctx context.Context) (T, error) { return b.refreshFunc(ctx, key) }, b.maxAge) b.cache[key] = c @@ -78,7 +77,7 @@ func (b *MapCache0[T]) StoreOrRefresh(ctx context.Context, key string) (T, error b.lock.Unlock() return c.Refresh(ctx) } - c = refreshcache0.NewRefreshCache[T](func(ctx context.Context) (T, error) { + c = refreshcache0.NewRefreshCache(func(ctx context.Context) (T, error) { return b.refreshFunc(ctx, key) }, b.maxAge) b.cache[key] = c @@ -107,7 +106,7 @@ func (b *MapCache0[T]) LoadOrNewCache(key string) *refreshcache0.RefreshCache[T] b.lock.Unlock() return c } - c = refreshcache0.NewRefreshCache[T](func(ctx context.Context) (T, error) { + c = refreshcache0.NewRefreshCache(func(ctx context.Context) (T, error) { return b.refreshFunc(ctx, key) }, b.maxAge) b.cache[key] = c @@ -115,7 +114,11 @@ func (b *MapCache0[T]) LoadOrNewCache(key string) *refreshcache0.RefreshCache[T] return c } -func (b *MapCache0[T]) LoadOrStoreWithDynamicFunc(ctx context.Context, key string, refreshFunc MapRefreshFunc0[T]) (T, error) { +func (b *MapCache0[T]) LoadOrStoreWithDynamicFunc( + ctx context.Context, + key string, + refreshFunc MapRefreshFunc0[T], +) (T, error) { b.lock.RLock() c, loaded := b.cache[key] if loaded { @@ -133,7 +136,7 @@ func (b *MapCache0[T]) LoadOrStoreWithDynamicFunc(ctx context.Context, key strin return refreshFunc(ctx, key) }) } - c = refreshcache0.NewRefreshCache[T](func(ctx context.Context) (T, error) { + c = refreshcache0.NewRefreshCache(func(ctx context.Context) (T, error) { return b.refreshFunc(ctx, key) }, b.maxAge) b.cache[key] = c @@ -143,7 +146,11 @@ func (b *MapCache0[T]) LoadOrStoreWithDynamicFunc(ctx context.Context, key strin }) } -func (b *MapCache0[T]) StoreOrRefreshWithDynamicFunc(ctx context.Context, key string, refreshFunc MapRefreshFunc0[T]) (T, error) { +func (b *MapCache0[T]) StoreOrRefreshWithDynamicFunc( + ctx context.Context, + key string, + refreshFunc MapRefreshFunc0[T], +) (T, error) { b.lock.RLock() c, ok := b.cache[key] if ok { @@ -161,7 +168,7 @@ func (b *MapCache0[T]) StoreOrRefreshWithDynamicFunc(ctx context.Context, key st return refreshFunc(ctx, key) }) } - c = refreshcache0.NewRefreshCache[T](func(ctx context.Context) (T, error) { + c = refreshcache0.NewRefreshCache(func(ctx context.Context) (T, error) { return b.refreshFunc(ctx, key) }, b.maxAge) b.cache[key] = c diff --git a/internal/cache/emby.go b/internal/cache/emby.go index 77ff7cbe..b0a55b90 100644 --- a/internal/cache/emby.go +++ b/internal/cache/emby.go @@ -32,7 +32,7 @@ type EmbyUserCacheData struct { } func NewEmbyUserCache(userID string) *EmbyUserCache { - return newMapCache0(func(ctx context.Context, key string) (*EmbyUserCacheData, error) { + return newMapCache0(func(_ context.Context, key string) (*EmbyUserCacheData, error) { return EmbyAuthorizationCacheWithUserIDInitFunc(userID, key) }, -1) } @@ -84,16 +84,19 @@ func NewEmbyMovieCache(movie *model.Movie, subPath string) *EmbyMovieCache { return cache } -func NewEmbyMovieClearCacheFunc(movie *model.Movie, subPath string) func(ctx context.Context, args *EmbyUserCache) error { +func NewEmbyMovieClearCacheFunc( + movie *model.Movie, + _ string, +) func(ctx context.Context, args *EmbyUserCache) error { return func(ctx context.Context, args *EmbyUserCache) error { - if !movie.MovieBase.VendorInfo.Emby.Transcode { + if !movie.VendorInfo.Emby.Transcode { return nil } if args == nil { return errors.New("need emby user cache") } - serverID, err := movie.MovieBase.VendorInfo.Emby.ServerID() + serverID, err := movie.VendorInfo.Emby.ServerID() if err != nil { return err } @@ -123,7 +126,10 @@ func NewEmbyMovieClearCacheFunc(movie *model.Movie, subPath string) func(ctx con } } -func NewEmbyMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx context.Context, args *EmbyUserCache) (*EmbyMovieCacheData, error) { +func NewEmbyMovieCacheInitFunc( + movie *model.Movie, + subPath string, +) func(ctx context.Context, args *EmbyUserCache) (*EmbyMovieCacheData, error) { return func(ctx context.Context, args *EmbyUserCache) (*EmbyMovieCacheData, error) { if err := validateEmbyArgs(args, movie, subPath); err != nil { return nil, err @@ -148,8 +154,8 @@ func NewEmbyMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx cont } resp := &EmbyMovieCacheData{ - Sources: make([]EmbySource, len(data.MediaSourceInfo)), - TranscodeSessionID: data.PlaySessionID, + Sources: make([]EmbySource, len(data.GetMediaSourceInfo())), + TranscodeSessionID: data.GetPlaySessionID(), } u, err := url.Parse(aucd.Host) @@ -157,7 +163,7 @@ func NewEmbyMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx cont return nil, err } - for i, v := range data.MediaSourceInfo { + for i, v := range data.GetMediaSourceInfo() { source, err := processMediaSource(v, movie, aucd, truePath, u) if err != nil { return nil, err @@ -183,7 +189,7 @@ func validateEmbyArgs(args *EmbyUserCache, movie *model.Movie, subPath string) e } func getEmbyServerIDAndPath(movie *model.Movie, subPath string) (string, string, error) { - serverID, truePath, err := movie.MovieBase.VendorInfo.Emby.ServerIDAndFilePath() + serverID, truePath, err := movie.VendorInfo.Emby.ServerIDAndFilePath() if err != nil { return "", "", err } @@ -193,7 +199,11 @@ func getEmbyServerIDAndPath(movie *model.Movie, subPath string) (string, string, return serverID, truePath, nil } -func getPlaybackInfo(ctx context.Context, aucd *EmbyUserCacheData, truePath string) (*emby.PlaybackInfoResp, error) { +func getPlaybackInfo( + ctx context.Context, + aucd *EmbyUserCacheData, + truePath string, +) (*emby.PlaybackInfoResp, error) { cli := vendor.LoadEmbyClient(aucd.Backend) data, err := cli.PlaybackInfo(ctx, &emby.PlaybackInfoReq{ Host: aucd.Host, @@ -207,20 +217,27 @@ func getPlaybackInfo(ctx context.Context, aucd *EmbyUserCacheData, truePath stri return data, nil } -func processMediaSource(v *emby.MediaSourceInfo, movie *model.Movie, aucd *EmbyUserCacheData, truePath string, u *url.URL) (*EmbySource, error) { - source := &EmbySource{Name: v.Name} - - if v.TranscodingUrl != "" { - source.URL = fmt.Sprintf("%s/emby%s", aucd.Host, v.TranscodingUrl) +func processMediaSource( + v *emby.MediaSourceInfo, + _ *model.Movie, + aucd *EmbyUserCacheData, + truePath string, + u *url.URL, +) (*EmbySource, error) { + source := &EmbySource{Name: v.GetName()} + + switch { + case v.GetTranscodingUrl() != "": + source.URL = fmt.Sprintf("%s/emby%s", aucd.Host, v.GetTranscodingUrl()) source.IsTranscode = true - } else if v.DirectPlayUrl != "" { - source.URL = fmt.Sprintf("%s/emby%s", aucd.Host, v.DirectPlayUrl) + case v.GetDirectPlayUrl() != "": + source.URL = fmt.Sprintf("%s/emby%s", aucd.Host, v.GetDirectPlayUrl()) source.IsTranscode = false - } else { - if v.Container == "" { + default: + if v.GetContainer() == "" { return nil, nil } - result, err := url.JoinPath("emby", "Videos", truePath, "stream."+v.Container) + result, err := url.JoinPath("emby", "Videos", truePath, "stream."+v.GetContainer()) if err != nil { return nil, err } @@ -228,7 +245,7 @@ func processMediaSource(v *emby.MediaSourceInfo, movie *model.Movie, aucd *EmbyU query := url.Values{} query.Set("api_key", aucd.APIKey) query.Set("Static", "true") - query.Set("MediaSourceId", v.Id) + query.Set("MediaSourceId", v.GetId()) u.RawQuery = query.Encode() source.URL = u.String() } @@ -236,15 +253,27 @@ func processMediaSource(v *emby.MediaSourceInfo, movie *model.Movie, aucd *EmbyU return source, nil } -func processEmbySubtitles(v *emby.MediaSourceInfo, truePath string, u *url.URL) []*EmbySubtitleCache { - subtitles := make([]*EmbySubtitleCache, 0, len(v.MediaStreamInfo)) - for _, msi := range v.MediaStreamInfo { - if msi.Type != "Subtitle" { +func processEmbySubtitles( + v *emby.MediaSourceInfo, + truePath string, + u *url.URL, +) []*EmbySubtitleCache { + subtitles := make([]*EmbySubtitleCache, 0, len(v.GetMediaStreamInfo())) + for _, msi := range v.GetMediaStreamInfo() { + if msi.GetType() != "Subtitle" { continue } subtutleType := "srt" - result, err := url.JoinPath("emby", "Videos", truePath, v.Id, "Subtitles", strconv.Itoa(int(msi.Index)), "Stream."+subtutleType) + result, err := url.JoinPath( + "emby", + "Videos", + truePath, + v.GetId(), + "Subtitles", + strconv.FormatUint(msi.GetIndex(), 10), + "Stream."+subtutleType, + ) if err != nil { continue } @@ -252,12 +281,12 @@ func processEmbySubtitles(v *emby.MediaSourceInfo, truePath string, u *url.URL) u.RawQuery = "" url := u.String() - name := msi.DisplayTitle + name := msi.GetDisplayTitle() if name == "" { - if msi.Title != "" { - name = msi.Title + if msi.GetTitle() != "" { + name = msi.GetTitle() } else { - name = msi.DisplayLanguage + name = msi.GetDisplayLanguage() } } diff --git a/internal/captcha/captcha.go b/internal/captcha/captcha.go index 3b8d7ac3..1bc41a9c 100644 --- a/internal/captcha/captcha.go +++ b/internal/captcha/captcha.go @@ -7,5 +7,8 @@ import ( var Captcha *base64Captcha.Captcha func init() { - Captcha = base64Captcha.NewCaptcha(base64Captcha.DefaultDriverDigit, base64Captcha.DefaultMemStore) + Captcha = base64Captcha.NewCaptcha( + base64Captcha.DefaultDriverDigit, + base64Captcha.DefaultMemStore, + ) } diff --git a/internal/conf/db.go b/internal/conf/db.go index 6f468073..beb17a6a 100644 --- a/internal/conf/db.go +++ b/internal/conf/db.go @@ -11,19 +11,19 @@ const ( //nolint:tagliatelle type DatabaseConfig struct { Type DatabaseType `env:"DATABASE_TYPE" hc:"support sqlite3, mysql, postgres" lc:"default: sqlite3" yaml:"type"` - Host string `env:"DATABASE_HOST" hc:"when type is not sqlite3, and port is 0, it will use unix socket file" yaml:"host"` - Port uint16 `env:"DATABASE_PORT" yaml:"port"` - User string `env:"DATABASE_USER" yaml:"user"` - Password string `env:"DATABASE_PASSWORD" yaml:"password"` + Host string `env:"DATABASE_HOST" hc:"when type is not sqlite3, and port is 0, it will use unix socket file" yaml:"host"` + Port uint16 `env:"DATABASE_PORT" yaml:"port"` + User string `env:"DATABASE_USER" yaml:"user"` + Password string `env:"DATABASE_PASSWORD" yaml:"password"` Name string `env:"DATABASE_NAME" hc:"when type is sqlite3, it will use sqlite db file or memory" lc:"default: synctv" yaml:"name"` - SslMode string `env:"DATABASE_SSL_MODE" hc:"mysql: true, false, skip-verify, preferred, postgres: disable, require, verify-ca, verify-full" yaml:"ssl_mode"` + SslMode string `env:"DATABASE_SSL_MODE" hc:"mysql: true, false, skip-verify, preferred, postgres: disable, require, verify-ca, verify-full" yaml:"ssl_mode"` CustomDSN string `env:"DATABASE_CUSTOM_DSN" hc:"when not empty, it will ignore other config" yaml:"custom_dsn"` MaxIdleConns int `env:"DATABASE_MAX_IDLE_CONNS" hc:"sqlite3 does not support setting connection parameters" yaml:"max_idle_conns"` - MaxOpenConns int `env:"DATABASE_MAX_OPEN_CONNS" yaml:"max_open_conns"` - ConnMaxLifetime string `env:"DATABASE_CONN_MAX_LIFETIME" yaml:"conn_max_lifetime"` - ConnMaxIdleTime string `env:"DATABASE_CONN_MAX_IDLE_TIME" yaml:"conn_max_idle_time"` + MaxOpenConns int `env:"DATABASE_MAX_OPEN_CONNS" yaml:"max_open_conns"` + ConnMaxLifetime string `env:"DATABASE_CONN_MAX_LIFETIME" yaml:"conn_max_lifetime"` + ConnMaxIdleTime string `env:"DATABASE_CONN_MAX_IDLE_TIME" yaml:"conn_max_idle_time"` } func DefaultDatabaseConfig() DatabaseConfig { diff --git a/internal/conf/log.go b/internal/conf/log.go index 0c4d8a36..734b7b11 100644 --- a/internal/conf/log.go +++ b/internal/conf/log.go @@ -3,9 +3,9 @@ package conf //nolint:tagliatelle type LogConfig struct { Enable bool `env:"LOG_ENABLE" yaml:"enable"` - LogFormat string `env:"LOG_FORMAT" hc:"can be set: text | json" yaml:"log_format"` - FilePath string `env:"LOG_FILE_PATH" hc:"if it is a relative path, the data-dir directory will be used." yaml:"file_path"` - MaxSize int `cm:"mb" env:"LOG_MAX_SIZE" hc:"max size per log file" yaml:"max_size"` + LogFormat string `env:"LOG_FORMAT" yaml:"log_format" hc:"can be set: text | json"` + FilePath string `env:"LOG_FILE_PATH" yaml:"file_path" hc:"if it is a relative path, the data-dir directory will be used."` + MaxSize int `env:"LOG_MAX_SIZE" yaml:"max_size" hc:"max size per log file" cm:"mb"` MaxBackups int `env:"LOG_MAX_BACKUPS" yaml:"max_backups"` MaxAge int `env:"LOG_MAX_AGE" yaml:"max_age"` Compress bool `env:"LOG_COMPRESS" yaml:"compress"` diff --git a/internal/conf/reatLimit.go b/internal/conf/reatLimit.go index f6fd587a..8984b5a7 100644 --- a/internal/conf/reatLimit.go +++ b/internal/conf/reatLimit.go @@ -2,11 +2,11 @@ package conf //nolint:tagliatelle type RateLimitConfig struct { - Enable bool `env:"SERVER_RATE_LIMIT_ENABLE" lc:"default: false" yaml:"enable"` - Period string `env:"SERVER_RATE_LIMIT_PERIOD" yaml:"period"` - Limit int64 `env:"SERVER_RATE_LIMIT_LIMIT" yaml:"limit"` - TrustForwardHeader bool `env:"SERVER_RATE_LIMIT_TRUST_FORWARD_HEADER" hc:"configure the limiter to trust X-Real-IP and X-Forwarded-For headers. Please be advised that using this option could be insecure (ie: spoofed) if your reverse proxy is not configured properly to forward a trustworthy client IP." lc:"default: false" yaml:"trust_forward_header"` - TrustedClientIPHeader string `env:"SERVER_RATE_LIMIT_TRUSTED_CLIENT_IP_HEADER" hc:"configure the limiter to use a custom header to obtain user IP. Please be advised that using this option could be insecure (ie: spoofed) if your reverse proxy is not configured properly to forward a trustworthy client IP." yaml:"trusted_client_ip_header"` + Enable bool `env:"SERVER_RATE_LIMIT_ENABLE" lc:"default: false" yaml:"enable"` + Period string `env:"SERVER_RATE_LIMIT_PERIOD" yaml:"period"` + Limit int64 `env:"SERVER_RATE_LIMIT_LIMIT" yaml:"limit"` + TrustForwardHeader bool `env:"SERVER_RATE_LIMIT_TRUST_FORWARD_HEADER" lc:"default: false" yaml:"trust_forward_header" hc:"configure the limiter to trust X-Real-IP and X-Forwarded-For headers. Please be advised that using this option could be insecure (ie: spoofed) if your reverse proxy is not configured properly to forward a trustworthy client IP."` + TrustedClientIPHeader string `env:"SERVER_RATE_LIMIT_TRUSTED_CLIENT_IP_HEADER" yaml:"trusted_client_ip_header" hc:"configure the limiter to use a custom header to obtain user IP. Please be advised that using this option could be insecure (ie: spoofed) if your reverse proxy is not configured properly to forward a trustworthy client IP."` } func DefaultRateLimitConfig() RateLimitConfig { diff --git a/internal/conf/server.go b/internal/conf/server.go index 151f0375..bdc4e798 100644 --- a/internal/conf/server.go +++ b/internal/conf/server.go @@ -2,14 +2,14 @@ package conf //nolint:tagliatelle type ServerConfig struct { - HTTP HttpServerConfig `yaml:"http"` - RTMP RtmpServerConfig `yaml:"rtmp"` - ProxyCachePath string `env:"SERVER_PROXY_CACHE_PATH" hc:"proxy cache path storage path, empty means use memory cache" yaml:"proxy_cache_path"` - ProxyCacheSize string `env:"SERVER_PROXY_CACHE_SIZE" hc:"proxy cache max size, example: 1MB 1GB, default 1GB" yaml:"proxy_cache_size"` + HTTP HTTPServerConfig `yaml:"http"` + RTMP RTMPServerConfig `yaml:"rtmp"` + ProxyCachePath string `yaml:"proxy_cache_path" env:"SERVER_PROXY_CACHE_PATH" hc:"proxy cache path storage path, empty means use memory cache"` + ProxyCacheSize string `yaml:"proxy_cache_size" env:"SERVER_PROXY_CACHE_SIZE" hc:"proxy cache max size, example: 1MB 1GB, default 1GB"` } //nolint:tagliatelle -type HttpServerConfig struct { +type HTTPServerConfig struct { Listen string `env:"SERVER_LISTEN" yaml:"listen"` Port uint16 `env:"SERVER_PORT" yaml:"port"` @@ -17,21 +17,21 @@ type HttpServerConfig struct { KeyPath string `env:"SERVER_KEY_PATH" yaml:"key_path"` } -type RtmpServerConfig struct { +type RTMPServerConfig struct { Enable bool `env:"RTMP_ENABLE" yaml:"enable"` - Listen string `env:"RTMP_LISTEN" lc:"default use http listen" yaml:"listen"` - Port uint16 `env:"RTMP_PORT" lc:"default use server port" yaml:"port"` + Listen string `env:"RTMP_LISTEN" yaml:"listen" lc:"default use http listen"` + Port uint16 `env:"RTMP_PORT" yaml:"port" lc:"default use server port"` } func DefaultServerConfig() ServerConfig { return ServerConfig{ - HTTP: HttpServerConfig{ + HTTP: HTTPServerConfig{ Listen: "0.0.0.0", Port: 8080, CertPath: "", KeyPath: "", }, - RTMP: RtmpServerConfig{ + RTMP: RTMPServerConfig{ Enable: true, Port: 0, }, diff --git a/internal/db/db.go b/internal/db/db.go index 2b23a09f..347bd4df 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -8,7 +8,6 @@ import ( "github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/utils" - // import fastjson serializer _ "github.com/synctv-org/synctv/utils/fastJSONSerializer" "gorm.io/gorm" @@ -57,7 +56,12 @@ func initGuestUser() error { if err == nil || !errors.Is(err, gorm.ErrRecordNotFound) { return err } - u, err := CreateUser("guest", utils.RandString(32), WithRole(model.RoleUser), WithID(GuestUserID)) + u, err := CreateUser( + "guest", + utils.RandString(32), + WithRole(model.RoleUser), + WithID(GuestUserID), + ) log.Infof("init guest user:\nid: %s\nusername: %s", u.ID, u.Username) return err } @@ -177,14 +181,14 @@ func WhereCreatorID(creatorID string) func(db *gorm.DB) *gorm.DB { } // column cannot be a user parameter -func WhereEqual(column string, value interface{}) func(db *gorm.DB) *gorm.DB { +func WhereEqual(column string, value any) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { return db.Where("? = ?", column, value) } } // column cannot be a user parameter -func WhereLike(column string, value string) func(db *gorm.DB) *gorm.DB { +func WhereLike(column, value string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { case conf.DatabaseTypePostgres: @@ -199,36 +203,72 @@ func WhereMovieNameLikeOrURLLike(name, url string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { case conf.DatabaseTypePostgres: - return db.Where("base_name ILIKE ? OR base_url ILIKE ?", utils.LIKE(name), utils.LIKE(url)) + return db.Where( + "base_name ILIKE ? OR base_url ILIKE ?", + utils.LIKE(name), + utils.LIKE(url), + ) default: - return db.Where("base_name LIKE ? OR base_url LIKE ?", utils.LIKE(name), utils.LIKE(url)) + return db.Where( + "base_name LIKE ? OR base_url LIKE ?", + utils.LIKE(name), + utils.LIKE(url), + ) } } } -func WhereRoomNameLikeOrCreatorInOrIDLike(name string, ids []string, id string) func(db *gorm.DB) *gorm.DB { +func WhereRoomNameLikeOrCreatorInOrIDLike( + name string, + ids []string, + id string, +) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { case conf.DatabaseTypePostgres: - return db.Where("name ILIKE ? OR creator_id IN ? OR id ILIKE ?", utils.LIKE(name), ids, id) + return db.Where( + "name ILIKE ? OR creator_id IN ? OR id ILIKE ?", + utils.LIKE(name), + ids, + id, + ) default: - return db.Where("name LIKE ? OR creator_id IN ? OR id LIKE ?", utils.LIKE(name), ids, id) + return db.Where( + "name LIKE ? OR creator_id IN ? OR id LIKE ?", + utils.LIKE(name), + ids, + id, + ) } } } -func WhereRoomNameLikeOrCreatorInOrRoomsIDLike(name string, ids []string, id string) func(db *gorm.DB) *gorm.DB { +func WhereRoomNameLikeOrCreatorInOrRoomsIDLike( + name string, + ids []string, + id string, +) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { case conf.DatabaseTypePostgres: - return db.Where("name ILIKE ? OR creator_id IN ? OR rooms.id ILIKE ?", utils.LIKE(name), ids, id) + return db.Where( + "name ILIKE ? OR creator_id IN ? OR rooms.id ILIKE ?", + utils.LIKE(name), + ids, + id, + ) default: - return db.Where("name LIKE ? OR creator_id IN ? OR rooms.id LIKE ?", utils.LIKE(name), ids, id) + return db.Where( + "name LIKE ? OR creator_id IN ? OR rooms.id LIKE ?", + utils.LIKE(name), + ids, + id, + ) } } } -func WhereRoomNameLikeOrIDLike(name string, id string) func(db *gorm.DB) *gorm.DB { +func WhereRoomNameLikeOrIDLike(name, id string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { case conf.DatabaseTypePostgres: diff --git a/internal/db/member.go b/internal/db/member.go index 3de6ce42..5b49a7c1 100644 --- a/internal/db/member.go +++ b/internal/db/member.go @@ -23,19 +23,26 @@ func WithRoomMemberRole(role model.RoomMemberRole) CreateRoomMemberRelationConfi } } -func WithRoomMemberPermissions(permissions model.RoomMemberPermission) CreateRoomMemberRelationConfig { +func WithRoomMemberPermissions( + permissions model.RoomMemberPermission, +) CreateRoomMemberRelationConfig { return func(r *model.RoomMember) { r.Permissions = permissions } } -func WithRoomMemberAdminPermissions(permissions model.RoomAdminPermission) CreateRoomMemberRelationConfig { +func WithRoomMemberAdminPermissions( + permissions model.RoomAdminPermission, +) CreateRoomMemberRelationConfig { return func(r *model.RoomMember) { r.AdminPermissions = permissions } } -func FirstOrCreateRoomMemberRelation(roomID, userID string, conf ...CreateRoomMemberRelationConfig) (*model.RoomMember, error) { +func FirstOrCreateRoomMemberRelation( + roomID, userID string, + conf ...CreateRoomMemberRelationConfig, +) (*model.RoomMember, error) { roomMemberRelation := &model.RoomMember{} d := &model.RoomMember{ RoomID: roomID, @@ -48,7 +55,10 @@ func FirstOrCreateRoomMemberRelation(roomID, userID string, conf ...CreateRoomMe for _, c := range conf { c(d) } - err := db.Where("room_id = ? AND user_id = ?", roomID, userID).Attrs(d).FirstOrCreate(roomMemberRelation).Error + err := db.Where("room_id = ? AND user_id = ?", roomID, userID). + Attrs(d). + FirstOrCreate(roomMemberRelation). + Error return roomMemberRelation, err } @@ -74,7 +84,9 @@ func RoomBanMember(roomID, userID string) error { } func RoomUnbanMember(roomID, userID string) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("status", model.RoomMemberStatusActive) + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("status", model.RoomMemberStatusActive) return HandleUpdateResult(result, ErrRoomMemberNotFound) } @@ -91,57 +103,78 @@ func DeleteRoomMember(roomID, userID string) error { return HandleUpdateResult(result, ErrRoomMemberNotFound) } -func SetMemberPermissions(roomID string, userID string, permission model.RoomMemberPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("permissions", permission) +func SetMemberPermissions(roomID, userID string, permission model.RoomMemberPermission) error { + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("permissions", permission) return HandleUpdateResult(result, ErrRoomMemberNotFound) } -func AddMemberPermissions(roomID string, userID string, permission model.RoomMemberPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("permissions", db.Raw("permissions | ?", permission)) +func AddMemberPermissions(roomID, userID string, permission model.RoomMemberPermission) error { + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("permissions", db.Raw("permissions | ?", permission)) return HandleUpdateResult(result, ErrRoomMemberNotFound) } -func RemoveMemberPermissions(roomID string, userID string, permission model.RoomMemberPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("permissions", db.Raw("permissions & ?", ^permission)) +func RemoveMemberPermissions(roomID, userID string, permission model.RoomMemberPermission) error { + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("permissions", db.Raw("permissions & ?", ^permission)) return HandleUpdateResult(result, ErrRoomMemberNotFound) } -// func GetAllRoomMembersRelationCount(roomID string, scopes ...func(*gorm.DB) *gorm.DB) (int64, error) { +// func GetAllRoomMembersRelationCount(roomID string, scopes ...func(*gorm.DB) *gorm.DB) (int64, +// error) { // var count int64 -// err := db.Model(&model.RoomMember{}).Where("room_id = ?", roomID).Scopes(scopes...).Count(&count).Error +// err := db.Model(&model.RoomMember{}).Where("room_id = ?", +// roomID).Scopes(scopes...).Count(&count).Error // return count, err // } func RoomSetAdminPermissions(roomID, userID string, permissions model.RoomAdminPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("admin_permissions", permissions) + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("admin_permissions", permissions) return HandleUpdateResult(result, ErrRoomMemberNotFound) } func RoomAddAdminPermissions(roomID, userID string, permissions model.RoomAdminPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("admin_permissions", db.Raw("admin_permissions | ?", permissions)) + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("admin_permissions", db.Raw("admin_permissions | ?", permissions)) return HandleUpdateResult(result, ErrRoomMemberNotFound) } -func RoomRemoveAdminPermissions(roomID, userID string, permissions model.RoomAdminPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Update("admin_permissions", db.Raw("admin_permissions & ?", ^permissions)) +func RoomRemoveAdminPermissions( + roomID, userID string, + permissions model.RoomAdminPermission, +) error { + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Update("admin_permissions", db.Raw("admin_permissions & ?", ^permissions)) return HandleUpdateResult(result, ErrRoomMemberNotFound) } func RoomSetAdmin(roomID, userID string, permissions model.RoomAdminPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Updates(map[string]interface{}{ - "role": model.RoomMemberRoleAdmin, - "permissions": model.AllPermissions, - "admin_permissions": permissions, - }) + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Updates(map[string]any{ + "role": model.RoomMemberRoleAdmin, + "permissions": model.AllPermissions, + "admin_permissions": permissions, + }) return HandleUpdateResult(result, ErrRoomMemberNotFound) } func RoomSetMember(roomID, userID string, permissions model.RoomMemberPermission) error { - result := db.Model(&model.RoomMember{}).Where("room_id = ? AND user_id = ?", roomID, userID).Updates(map[string]interface{}{ - "role": model.RoomMemberRoleMember, - "permissions": permissions, - "admin_permissions": model.NoAdminPermission, - }) + result := db.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, userID). + Updates(map[string]any{ + "role": model.RoomMemberRoleMember, + "permissions": permissions, + "admin_permissions": model.NoAdminPermission, + }) return HandleUpdateResult(result, ErrRoomMemberNotFound) } diff --git a/internal/db/movie.go b/internal/db/movie.go index 1336c734..d9b93e34 100644 --- a/internal/db/movie.go +++ b/internal/db/movie.go @@ -29,13 +29,21 @@ func WithParentMovieID(parentMovieID string) func(*gorm.DB) *gorm.DB { func GetMoviesByRoomID(roomID string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model.Movie, error) { var movies []*model.Movie - err := db.Where("room_id = ?", roomID).Order("position ASC").Scopes(scopes...).Find(&movies).Error + err := db.Where("room_id = ?", roomID). + Order("position ASC"). + Scopes(scopes...). + Find(&movies). + Error return movies, err } func GetMoviesCountByRoomID(roomID string, scopes ...func(*gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := db.Model(&model.Movie{}).Where("room_id = ?", roomID).Scopes(scopes...).Count(&count).Error + err := db.Model(&model.Movie{}). + Where("room_id = ?", roomID). + Scopes(scopes...). + Count(&count). + Error return count, err } @@ -65,12 +73,19 @@ func DeleteMoviesByRoomIDAndParentID(roomID, parentID string) error { } func UpdateMovie(movie *model.Movie, columns ...clause.Column) error { - result := db.Model(movie).Clauses(clause.Returning{Columns: columns}).Where("room_id = ? AND id = ?", movie.RoomID, movie.ID).Updates(movie) + result := db.Model(movie). + Clauses(clause.Returning{Columns: columns}). + Where("room_id = ? AND id = ?", movie.RoomID, movie.ID). + Updates(movie) return HandleUpdateResult(result, ErrRoomOrMovieNotFound) } func SaveMovie(movie *model.Movie, columns ...clause.Column) error { - result := db.Model(movie).Clauses(clause.Returning{Columns: columns}).Where("room_id = ? AND id = ?", movie.RoomID, movie.ID).Omit("created_at").Save(movie) + result := db.Model(movie). + Clauses(clause.Returning{Columns: columns}). + Where("room_id = ? AND id = ?", movie.RoomID, movie.ID). + Omit("created_at"). + Save(movie) return HandleUpdateResult(result, ErrRoomOrMovieNotFound) } @@ -86,11 +101,15 @@ func SwapMoviePositions(roomID, movie1ID, movie2ID string) error { movie1.Position, movie2.Position = movie2.Position, movie1.Position - result1 := tx.Model(&movie1).Where("room_id = ? AND id = ?", roomID, movie1ID).Update("position", movie1.Position) + result1 := tx.Model(&movie1). + Where("room_id = ? AND id = ?", roomID, movie1ID). + Update("position", movie1.Position) if err := HandleUpdateResult(result1, ErrRoomOrMovieNotFound); err != nil { return err } - result2 := tx.Model(&movie2).Where("room_id = ? AND id = ?", roomID, movie2ID).Update("position", movie2.Position) + result2 := tx.Model(&movie2). + Where("room_id = ? AND id = ?", roomID, movie2ID). + Update("position", movie2.Position) return HandleUpdateResult(result2, ErrRoomOrMovieNotFound) }) } diff --git a/internal/db/room.go b/internal/db/room.go index 61ebf3b8..e106fe38 100644 --- a/internal/db/room.go +++ b/internal/db/room.go @@ -60,7 +60,11 @@ func WithSettingHidden(hidden bool) CreateRoomConfig { } // if maxCount is 0, it will be ignored -func CreateRoom(name, password string, maxCount int64, conf ...CreateRoomConfig) (*model.Room, error) { +func CreateRoom( + name, password string, + maxCount int64, + conf ...CreateRoomConfig, +) (*model.Room, error) { r := &model.Room{ Name: name, Settings: model.DefaultRoomSettings(), @@ -69,7 +73,10 @@ func CreateRoom(name, password string, maxCount int64, conf ...CreateRoomConfig) c(r) } if password != "" { - hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err := bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } @@ -111,7 +118,10 @@ func GetRoomByID(id string) (*model.Room, error) { func CreateOrLoadRoomSettings(roomID string) (*model.RoomSettings, error) { var rs model.RoomSettings - err := db.Where(model.RoomSettings{ID: roomID}).Attrs(model.DefaultRoomSettings()).FirstOrCreate(&rs).Error + err := db.Where(model.RoomSettings{ID: roomID}). + Attrs(model.DefaultRoomSettings()). + FirstOrCreate(&rs). + Error return &rs, err } @@ -120,7 +130,7 @@ func SaveRoomSettings(roomID string, settings *model.RoomSettings) error { return HandleNotFound(db.Save(settings).Error, "room settings") } -func UpdateRoomSettings(roomID string, settings map[string]interface{}) (*model.RoomSettings, error) { +func UpdateRoomSettings(roomID string, settings map[string]any) (*model.RoomSettings, error) { var rs model.RoomSettings err := db.Model(&model.RoomSettings{ID: roomID}). Clauses(clause.Returning{}). @@ -138,7 +148,10 @@ func SetRoomPassword(roomID, password string) error { var hashedPassword []byte var err error if password != "" { - hashedPassword, err = bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err = bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return fmt.Errorf("failed to hash password: %w", err) } @@ -147,7 +160,9 @@ func SetRoomPassword(roomID, password string) error { } func SetRoomHashedPassword(roomID string, hashedPassword []byte) error { - result := db.Model(&model.Room{}).Where("id = ?", roomID).Update("hashed_password", hashedPassword) + result := db.Model(&model.Room{}). + Where("id = ?", roomID). + Update("hashed_password", hashedPassword) return HandleUpdateResult(result, ErrRoomNotFound) } diff --git a/internal/db/update.go b/internal/db/update.go index 80b383c4..d3cd4341 100644 --- a/internal/db/update.go +++ b/internal/db/update.go @@ -39,9 +39,12 @@ var dbVersions = map[string]dbVersion{ NextVersion: "0.0.3", Upgrade: func(db *gorm.DB) error { // alist and emby movies path are changed, so we need to delete them - _ = db.Exec("DELETE FROM movies WHERE base_vendor_info_vendor IN ('alist', 'emby')").Error + _ = db.Exec( + "DELETE FROM movies WHERE base_vendor_info_vendor IN ('alist', 'emby')", + ).Error _ = db.Migrator().DropTable("alist_vendors", "emby_vendors") - // delete all vendors, because we are going to change the more vendor table, e.g. bilibili_vendors + // delete all vendors, because we are going to change the more vendor table, e.g. + // bilibili_vendors _ = db.Migrator().DropTable("streaming_vendor_infos") return autoMigrate( new(model.AlistVendor), diff --git a/internal/db/user.go b/internal/db/user.go index 5eb82956..53371172 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -49,7 +49,11 @@ func WithDisableAutoAddUsernameSuffix() CreateUserConfig { } } -func CreateUserWithHashedPassword(username string, hashedPassword []byte, conf ...CreateUserConfig) (*model.User, error) { +func CreateUserWithHashedPassword( + username string, + hashedPassword []byte, + conf ...CreateUserConfig, +) (*model.User, error) { if username == "" { return nil, errors.New("username cannot be empty") } @@ -80,25 +84,34 @@ func CreateUserWithHashedPassword(username string, hashedPassword []byte, conf . return u, nil } -func CreateUser(username string, password string, conf ...CreateUserConfig) (*model.User, error) { +func CreateUser(username, password string, conf ...CreateUserConfig) (*model.User, error) { if username == "" { return nil, errors.New("username cannot be empty") } if password == "" { return nil, errors.New("password cannot be empty") } - hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err := bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } return CreateUserWithHashedPassword(username, hashedPassword, conf...) } -func CreateOrLoadUserWithProvider(username, password string, p string, puid string, conf ...CreateUserConfig) (*model.User, error) { +func CreateOrLoadUserWithProvider( + username, password, p, puid string, + conf ...CreateUserConfig, +) (*model.User, error) { if puid == "" { return nil, errors.New("provider user id cannot be empty") } - hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err := bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } @@ -128,7 +141,10 @@ func CreateOrLoadUserWithProvider(username, password string, p string, puid stri return user, nil } -func CreateUserWithEmail(username, password, email string, conf ...CreateUserConfig) (*model.User, error) { +func CreateUserWithEmail( + username, password, email string, + conf ...CreateUserConfig, +) (*model.User, error) { if email == "" { return nil, errors.New("email cannot be empty") } @@ -138,7 +154,7 @@ func CreateUserWithEmail(username, password, email string, conf ...CreateUserCon )...) } -func GetUserByProvider(p string, puid string) (*model.User, error) { +func GetUserByProvider(p, puid string) (*model.User, error) { var user model.User err := db.Joins("JOIN user_providers ON users.id = user_providers.user_id"). Where("user_providers.provider = ? AND user_providers.provider_user_id = ?", p, puid). @@ -152,7 +168,7 @@ func GetUserByEmail(email string) (*model.User, error) { return &user, HandleNotFound(err, ErrUserNotFound) } -func GetProviderUserID(p string, puid string) (string, error) { +func GetProviderUserID(p, puid string) (string, error) { var userID string err := db.Model(&model.UserProvider{}). Where("provider = ? AND provider_user_id = ?", p, puid). @@ -161,7 +177,7 @@ func GetProviderUserID(p string, puid string) (string, error) { return userID, HandleNotFound(err, ErrUserNotFound) } -func BindProvider(uid string, p string, puid string) error { +func BindProvider(uid, p, puid string) error { err := db.Create(&model.UserProvider{ UserID: uid, Provider: p, @@ -176,7 +192,7 @@ func BindProvider(uid string, p string, puid string) error { return nil } -func UnBindProvider(uid string, p string) error { +func UnBindProvider(uid, p string) error { return Transactional(func(tx *gorm.DB) error { var user model.User if err := tx.Preload("UserProviders").Where("id = ?", uid).First(&user).Error; err != nil { @@ -190,8 +206,10 @@ func UnBindProvider(uid string, p string) error { }) } -func BindEmail(id string, email string) error { - result := db.Model(&model.User{}).Where("id = ?", id).Update("email", model.EmptyNullString(email)) +func BindEmail(id, email string) error { + result := db.Model(&model.User{}). + Where("id = ?", id). + Update("email", model.EmptyNullString(email)) return HandleUpdateResult(result, ErrUserNotFound) } @@ -207,7 +225,9 @@ func UnbindEmail(uid string) error { if user.Email.String() == "" { return nil } - result := tx.Model(&model.User{}).Where("id = ?", uid).Update("email", model.EmptyNullString("")) + result := tx.Model(&model.User{}). + Where("id = ?", uid). + Update("email", model.EmptyNullString("")) return HandleUpdateResult(result, ErrUserNotFound) }) } @@ -227,18 +247,31 @@ func GetUserByUsername(username string) (*model.User, error) { return &user, HandleNotFound(err, ErrUserNotFound) } -func GetUserByUsernameLike(username string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model.User, error) { +func GetUserByUsernameLike( + username string, + scopes ...func(*gorm.DB) *gorm.DB, +) ([]*model.User, error) { var users []*model.User - err := db.Where("username LIKE ?", fmt.Sprintf("%%%s%%", username)).Scopes(scopes...).Find(&users).Error + err := db.Where("username LIKE ?", fmt.Sprintf("%%%s%%", username)). + Scopes(scopes...). + Find(&users). + Error if err != nil { return nil, fmt.Errorf("failed to get users by username like: %w", err) } return users, nil } -func GerUsersIDByUsernameLike(username string, scopes ...func(*gorm.DB) *gorm.DB) ([]string, error) { +func GerUsersIDByUsernameLike( + username string, + scopes ...func(*gorm.DB) *gorm.DB, +) ([]string, error) { var ids []string - err := db.Model(&model.User{}).Where("username LIKE ?", fmt.Sprintf("%%%s%%", username)).Scopes(scopes...).Pluck("id", &ids).Error + err := db.Model(&model.User{}). + Where("username LIKE ?", fmt.Sprintf("%%%s%%", username)). + Scopes(scopes...). + Pluck("id", &ids). + Error if err != nil { return nil, fmt.Errorf("failed to get user IDs by username like: %w", err) } @@ -247,16 +280,26 @@ func GerUsersIDByUsernameLike(username string, scopes ...func(*gorm.DB) *gorm.DB func GerUsersIDByIDLike(id string, scopes ...func(*gorm.DB) *gorm.DB) ([]string, error) { var ids []string - err := db.Model(&model.User{}).Where("id LIKE ?", utils.LIKE(id)).Scopes(scopes...).Pluck("id", &ids).Error + err := db.Model(&model.User{}). + Where("id LIKE ?", utils.LIKE(id)). + Scopes(scopes...). + Pluck("id", &ids). + Error if err != nil { return nil, fmt.Errorf("failed to get user IDs by ID like: %w", err) } return ids, nil } -func GetUserByIDOrUsernameLike(idOrUsername string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model.User, error) { +func GetUserByIDOrUsernameLike( + idOrUsername string, + scopes ...func(*gorm.DB) *gorm.DB, +) ([]*model.User, error) { var users []*model.User - err := db.Where("id = ? OR username LIKE ?", idOrUsername, fmt.Sprintf("%%%s%%", idOrUsername)).Scopes(scopes...).Find(&users).Error + err := db.Where("id = ? OR username LIKE ?", idOrUsername, fmt.Sprintf("%%%s%%", idOrUsername)). + Scopes(scopes...). + Find(&users). + Error if err != nil { return nil, fmt.Errorf("failed to get users by ID or username like: %w", err) } @@ -400,7 +443,7 @@ func SetUserRoleByID(userID string) error { return HandleUpdateResult(result, ErrUserNotFound) } -func SetUsernameByID(userID string, username string) error { +func SetUsernameByID(userID, username string) error { result := db.Model(&model.User{}).Where("id = ?", userID).Update("username", username) return HandleUpdateResult(result, ErrUserNotFound) } diff --git a/internal/db/vendorBackend.go b/internal/db/vendorBackend.go index d9a596e5..afb6c7e6 100644 --- a/internal/db/vendorBackend.go +++ b/internal/db/vendorBackend.go @@ -18,7 +18,9 @@ func CreateVendorBackend(backend *model.VendorBackend) error { } func updateVendorBackendEnabled(endpoint string, enabled bool) error { - result := db.Model(&model.VendorBackend{}).Where("backend_endpoint = ?", endpoint).Update("enabled", enabled) + result := db.Model(&model.VendorBackend{}). + Where("backend_endpoint = ?", endpoint). + Update("enabled", enabled) return HandleUpdateResult(result, "vendor backend") } @@ -27,7 +29,9 @@ func EnableVendorBackend(endpoint string) error { } func EnableVendorBackends(endpoints []string) error { - result := db.Model(&model.VendorBackend{}).Where("backend_endpoint IN ?", endpoints).Update("enabled", true) + result := db.Model(&model.VendorBackend{}). + Where("backend_endpoint IN ?", endpoints). + Update("enabled", true) return HandleUpdateResult(result, "vendor backends") } @@ -36,7 +40,9 @@ func DisableVendorBackend(endpoint string) error { } func DisableVendorBackends(endpoints []string) error { - result := db.Model(&model.VendorBackend{}).Where("backend_endpoint IN ?", endpoints).Update("enabled", false) + result := db.Model(&model.VendorBackend{}). + Where("backend_endpoint IN ?", endpoints). + Update("enabled", false) return HandleUpdateResult(result, "vendor backends") } @@ -59,7 +65,9 @@ func GetVendorBackend(endpoint string) (*model.VendorBackend, error) { func CreateOrSaveVendorBackend(backend *model.VendorBackend) (*model.VendorBackend, error) { return backend, Transactional(func(tx *gorm.DB) error { var existingBackend model.VendorBackend - err := tx.Where("backend_endpoint = ?", backend.Backend.Endpoint).First(&existingBackend).Error + err := tx.Where("backend_endpoint = ?", backend.Backend.Endpoint). + First(&existingBackend). + Error if errors.Is(err, gorm.ErrRecordNotFound) { return tx.Create(backend).Error } else if err != nil { diff --git a/internal/db/vendorRecord.go b/internal/db/vendorRecord.go index d10bb7d3..e0670040 100644 --- a/internal/db/vendorRecord.go +++ b/internal/db/vendorRecord.go @@ -37,7 +37,10 @@ func DeleteBilibiliVendor(userID string) error { return HandleUpdateResult(result, ErrVendorNotFound) } -func GetAlistVendors(userID string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model.AlistVendor, error) { +func GetAlistVendors( + userID string, + scopes ...func(*gorm.DB) *gorm.DB, +) ([]*model.AlistVendor, error) { var vendors []*model.AlistVendor err := db.Scopes(scopes...).Where("user_id = ?", userID).Find(&vendors).Error return vendors, err @@ -45,7 +48,11 @@ func GetAlistVendors(userID string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model func GetAlistVendorsCount(userID string, scopes ...func(*gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := db.Scopes(scopes...).Where("user_id = ?", userID).Model(&model.AlistVendor{}).Count(&count).Error + err := db.Scopes(scopes...). + Where("user_id = ?", userID). + Model(&model.AlistVendor{}). + Count(&count). + Error return count, err } @@ -72,7 +79,8 @@ func CreateOrSaveAlistVendor(vendorInfo *model.AlistVendor) (*model.AlistVendor, } func DeleteAlistVendor(userID, serverID string) error { - result := db.Where("user_id = ? AND server_id = ?", userID, serverID).Delete(&model.AlistVendor{}) + result := db.Where("user_id = ? AND server_id = ?", userID, serverID). + Delete(&model.AlistVendor{}) return HandleUpdateResult(result, ErrVendorNotFound) } @@ -84,7 +92,11 @@ func GetEmbyVendors(userID string, scopes ...func(*gorm.DB) *gorm.DB) ([]*model. func GetEmbyVendorsCount(userID string, scopes ...func(*gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := db.Scopes(scopes...).Where("user_id = ?", userID).Model(&model.EmbyVendor{}).Count(&count).Error + err := db.Scopes(scopes...). + Where("user_id = ?", userID). + Model(&model.EmbyVendor{}). + Count(&count). + Error return count, err } @@ -117,6 +129,7 @@ func CreateOrSaveEmbyVendor(vendorInfo *model.EmbyVendor) (*model.EmbyVendor, er } func DeleteEmbyVendor(userID, serverID string) error { - result := db.Where("user_id = ? AND server_id = ?", userID, serverID).Delete(&model.EmbyVendor{}) + result := db.Where("user_id = ? AND server_id = ?", userID, serverID). + Delete(&model.EmbyVendor{}) return HandleUpdateResult(result, ErrVendorNotFound) } diff --git a/internal/email/email.go b/internal/email/email.go index ef4c4663..cafe2d58 100644 --- a/internal/email/email.go +++ b/internal/email/email.go @@ -22,7 +22,9 @@ import ( var ( ErrEmailNotEnabled = errors.New("email is not enabled") - emailCaptcha *synccache.SyncCache[string, string] = synccache.NewSyncCache[string, string](time.Minute * 5) + emailCaptcha *synccache.SyncCache[string, string] = synccache.NewSyncCache[string, string]( + time.Minute * 5, + ) ) var ( @@ -30,9 +32,9 @@ var ( "enable_email", false, model.SettingGroupEmail, - settings.WithAfterSetBool(func(bs settings.BoolSetting, b bool) { + settings.WithAfterSetBool(func(_ settings.BoolSetting, b bool) { if !b { - closeSmtpPool() + closeSMTPPool() } }), ) @@ -140,7 +142,7 @@ func SendBindCaptchaEmail(userID, userEmail string) error { return errors.New("email is empty") } - pool, err := getSmtpPool() + pool, err := getSMTPPool() if err != nil { return err } @@ -204,7 +206,7 @@ func SendTestEmail(username, email string) error { return errors.New("email is empty") } - pool, err := getSmtpPool() + pool, err := getSMTPPool() if err != nil { return err } @@ -234,7 +236,7 @@ func SendSignupCaptchaEmail(email string) error { return errors.New("email is empty") } - pool, err := getSmtpPool() + pool, err := getSMTPPool() if err != nil { return err } @@ -312,7 +314,7 @@ func SendRetrievePasswordCaptchaEmail(userID, email, host string) error { } u.Path = `web/auth/reset` - pool, err := getSmtpPool() + pool, err := getSMTPPool() if err != nil { return err } diff --git a/internal/email/smtp.go b/internal/email/smtp.go index b80771ae..356180fb 100644 --- a/internal/email/smtp.go +++ b/internal/email/smtp.go @@ -21,7 +21,7 @@ var ( "smtp_host", "", model.SettingGroupEmail, - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, _ string) { lock.Lock() defer lock.Unlock() configChanged = true @@ -41,7 +41,7 @@ var ( } return nil }), - settings.WithAfterSetInt64(func(ss settings.Int64Setting, i int64) { + settings.WithAfterSetInt64(func(_ settings.Int64Setting, _ int64) { lock.Lock() defer lock.Unlock() configChanged = true @@ -60,7 +60,7 @@ var ( return errors.New("smtp protocol must be tcp, tls or ssl") } }), - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, _ string) { lock.Lock() defer lock.Unlock() configChanged = true @@ -70,7 +70,7 @@ var ( "smtp_username", "", model.SettingGroupEmail, - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, _ string) { lock.Lock() defer lock.Unlock() configChanged = true @@ -80,7 +80,7 @@ var ( "smtp_password", "", model.SettingGroupEmail, - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, _ string) { lock.Lock() defer lock.Unlock() configChanged = true @@ -90,7 +90,7 @@ var ( "smtp_from", "", model.SettingGroupEmail, - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, s string) { lock.Lock() defer lock.Unlock() @@ -112,7 +112,7 @@ var ( } return nil }), - settings.WithAfterSetInt64(func(ss settings.Int64Setting, i int64) { + settings.WithAfterSetInt64(func(_ settings.Int64Setting, _ int64) { lock.Lock() defer lock.Unlock() configChanged = true @@ -120,7 +120,8 @@ var ( ) ) -func newSmtpConfig() *smtp.Config { +//nolint:gosec +func newSMTPConfig() *smtp.Config { return &smtp.Config{ Host: smtpHost.Get(), Port: uint32(smtpPort.Get()), @@ -131,11 +132,11 @@ func newSmtpConfig() *smtp.Config { } } -func newSmtpPool() (*smtp.Pool, error) { - return smtp.NewSMTPPool(newSmtpConfig(), int(smtpPoolSize.Get())) +func newSMTPPool() (*smtp.Pool, error) { + return smtp.NewSMTPPool(newSMTPConfig(), int(smtpPoolSize.Get())) } -func getSmtpPool() (*smtp.Pool, error) { +func getSMTPPool() (*smtp.Pool, error) { lock.Lock() defer lock.Unlock() @@ -148,7 +149,7 @@ func getSmtpPool() (*smtp.Pool, error) { } if smtpPool == nil { - pool, err := newSmtpPool() + pool, err := newSMTPPool() if err != nil { return nil, err } @@ -158,7 +159,7 @@ func getSmtpPool() (*smtp.Pool, error) { return smtpPool, nil } -func closeSmtpPool() { +func closeSMTPPool() { lock.Lock() defer lock.Unlock() diff --git a/internal/model/movie.go b/internal/model/movie.go index ead74146..4b43181c 100644 --- a/internal/model/movie.go +++ b/internal/model/movie.go @@ -13,12 +13,12 @@ import ( type Movie struct { ID string `gorm:"primaryKey;type:char(32)" json:"id"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` + CreatedAt time.Time ` json:"-"` + UpdatedAt time.Time ` json:"-"` RoomID string `gorm:"not null;index;type:char(32)" json:"-"` CreatorID string `gorm:"index;type:char(32)" json:"creatorId"` Childrens []*Movie `gorm:"foreignKey:ParentID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"-"` - MovieBase `gorm:"embedded;embeddedPrefix:base_" json:"base"` + MovieBase `gorm:"embedded;embeddedPrefix:base_" json:"base"` Position uint `gorm:"not null" json:"-"` } @@ -35,17 +35,17 @@ func (m *Movie) Clone() *Movie { } } -func (m *Movie) BeforeCreate(tx *gorm.DB) error { +func (m *Movie) BeforeCreate(_ *gorm.DB) error { if m.ID == "" { m.ID = utils.SortUUID() } return nil } -func (m *Movie) BeforeSave(tx *gorm.DB) (err error) { +func (m *Movie) BeforeSave(tx *gorm.DB) error { if m.ParentID != "" { mv := &Movie{} - err = tx.Where("id = ?", m.ParentID).First(mv).Error + err := tx.Where("id = ?", m.ParentID).First(mv).Error if err != nil { return fmt.Errorf("load parent movie failed: %w", err) } @@ -56,7 +56,7 @@ func (m *Movie) BeforeSave(tx *gorm.DB) (err error) { return errors.New("parent is a dynamic folder, cannot add child") } } - return + return nil } type MoreSource struct { @@ -69,17 +69,17 @@ type MovieBase struct { VendorInfo VendorInfo `gorm:"embedded;embeddedPrefix:vendor_info_" json:"vendorInfo,omitempty"` Headers map[string]string `gorm:"serializer:fastjson;type:text" json:"headers,omitempty"` Subtitles map[string]*Subtitle `gorm:"serializer:fastjson;type:text" json:"subtitles,omitempty"` - URL string `gorm:"type:text" json:"url"` - Name string `gorm:"not null;type:text" json:"name"` - Type string `json:"type"` + URL string `gorm:"type:text" json:"url"` + Name string `gorm:"not null;type:text" json:"name"` + Type string ` json:"type"` ParentID EmptyNullString `gorm:"type:char(32)" json:"parentId"` MoreSources []*MoreSource `gorm:"serializer:fastjson;type:text" json:"moreSources,omitempty"` - Danmu string `gorm:"type:text" json:"danmu"` - StreamDanmu string `gorm:"type:text" json:"streamDanmu"` - Live bool `json:"live"` - Proxy bool `json:"proxy"` - RtmpSource bool `json:"rtmpSource"` - IsFolder bool `json:"isFolder"` + Danmu string `gorm:"type:text" json:"danmu"` + StreamDanmu string `gorm:"type:text" json:"streamDanmu"` + Live bool ` json:"live"` + Proxy bool ` json:"proxy"` + RtmpSource bool ` json:"rtmpSource"` + IsFolder bool ` json:"isFolder"` } func (m *MovieBase) IsM3u8() bool { @@ -208,11 +208,11 @@ func (b *BilibiliStreamingInfo) Validate() error { type AlistStreamingInfo struct { // {/}serverId/Path - Path string `gorm:"type:text" json:"path,omitempty"` - Password string `gorm:"type:varchar(64)" json:"password,omitempty"` + Path string `gorm:"type:text" json:"path,omitempty"` + Password string `gorm:"type:varchar(64)" json:"password,omitempty"` } -func GetAlistServerIDFromPath(path string) (serverID string, filePath string, err error) { +func GetAlistServerIDFromPath(path string) (serverID, filePath string, err error) { before, after, found := strings.Cut(strings.TrimLeft(path, "/"), "/") if !found { return "", path, errors.New("path is invalid") @@ -249,7 +249,7 @@ func (a *AlistStreamingInfo) Validate() error { return nil } -func (a *AlistStreamingInfo) BeforeSave(tx *gorm.DB) error { +func (a *AlistStreamingInfo) BeforeSave(_ *gorm.DB) error { if a.Password != "" { s, err := utils.CryptoToBase64([]byte(a.Password), utils.GenCryptoKey(a.Path)) if err != nil { @@ -260,7 +260,7 @@ func (a *AlistStreamingInfo) BeforeSave(tx *gorm.DB) error { return nil } -func (a *AlistStreamingInfo) AfterSave(tx *gorm.DB) error { +func (a *AlistStreamingInfo) AfterSave(_ *gorm.DB) error { if a.Password != "" { b, err := utils.DecryptoFromBase64(a.Password, utils.GenCryptoKey(a.Path)) if err != nil { @@ -277,11 +277,11 @@ func (a *AlistStreamingInfo) AfterFind(tx *gorm.DB) error { type EmbyStreamingInfo struct { // {/}serverId/ItemId - Path string `gorm:"type:varchar(52)" json:"path,omitempty"` - Transcode bool `json:"transcode,omitempty"` + Path string `gorm:"type:varchar(52)" json:"path,omitempty"` + Transcode bool ` json:"transcode,omitempty"` } -func GetEmbyServerIDFromPath(path string) (serverID string, filePath string, err error) { +func GetEmbyServerIDFromPath(path string) (serverID, filePath string, err error) { if s := strings.Split(strings.TrimLeft(path, "/"), "/"); len(s) == 2 { return s[0], s[1], nil } diff --git a/internal/model/room.go b/internal/model/room.go index 1661018a..da27c004 100644 --- a/internal/model/room.go +++ b/internal/model/room.go @@ -31,10 +31,10 @@ func (r RoomStatus) String() string { } type Room struct { - ID string `gorm:"primaryKey;type:char(32)" json:"id"` + ID string `gorm:"primaryKey;type:char(32)" json:"id"` CreatedAt time.Time UpdatedAt time.Time - Settings *RoomSettings `gorm:"foreignKey:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"settings"` + Settings *RoomSettings `gorm:"foreignKey:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"settings"` Name string `gorm:"not null;uniqueIndex;type:varchar(32)"` CreatorID string `gorm:"index;type:char(32)"` HashedPassword []byte @@ -44,7 +44,7 @@ type Room struct { Current *Current `gorm:"serializer:fastjson"` } -func (r *Room) BeforeCreate(tx *gorm.DB) error { +func (r *Room) BeforeCreate(_ *gorm.DB) error { if r.ID == "" { r.ID = utils.SortUUID() } @@ -56,7 +56,8 @@ func (r *Room) NeedPassword() bool { } func (r *Room) CheckPassword(password string) bool { - return !r.NeedPassword() || bcrypt.CompareHashAndPassword(r.HashedPassword, stream.StringToBytes(password)) == nil + return !r.NeedPassword() || + bcrypt.CompareHashAndPassword(r.HashedPassword, stream.StringToBytes(password)) == nil } func (r *Room) IsBanned() bool { @@ -75,8 +76,8 @@ func (r *Room) IsActive() bool { type RoomSettings struct { UpdatedAt time.Time `gorm:"autoUpdateTime" json:"-"` ID string `gorm:"primaryKey;type:char(32)" json:"-"` - UserDefaultPermissions RoomMemberPermission `json:"user_default_permissions"` - GuestPermissions RoomMemberPermission `json:"guest_permissions"` + UserDefaultPermissions RoomMemberPermission ` json:"user_default_permissions"` + GuestPermissions RoomMemberPermission ` json:"guest_permissions"` DisableGuest bool `gorm:"default:false" json:"disable_guest"` JoinNeedReview bool `gorm:"default:false" json:"join_need_review"` DisableJoinNewUser bool `gorm:"default:false" json:"disable_join_new_user"` diff --git a/internal/model/user.go b/internal/model/user.go index fff6c3e0..632521f3 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -39,7 +39,7 @@ func (r Role) String() string { } type User struct { - ID string `gorm:"primaryKey;type:char(32)" json:"id"` + ID string `gorm:"primaryKey;type:char(32)" json:"id"` CreatedAt time.Time UpdatedAt time.Time Username string `gorm:"not null;uniqueIndex;type:varchar(32)"` diff --git a/internal/model/vendorBackend.go b/internal/model/vendorBackend.go index fd00aed0..af7f6614 100644 --- a/internal/model/vendorBackend.go +++ b/internal/model/vendorBackend.go @@ -66,7 +66,7 @@ type BackendUsedBy struct { Emby bool `gorm:"default:false" json:"emby"` } -func (v *VendorBackend) BeforeSave(tx *gorm.DB) error { +func (v *VendorBackend) BeforeSave(_ *gorm.DB) error { key := utils.GenCryptoKey(v.Backend.Endpoint) var err error if v.Backend.JwtSecret != "" { @@ -92,7 +92,7 @@ func (v *VendorBackend) BeforeSave(tx *gorm.DB) error { return nil } -func (v *VendorBackend) AfterSave(tx *gorm.DB) error { +func (v *VendorBackend) AfterSave(_ *gorm.DB) error { key := utils.GenCryptoKey(v.Backend.Endpoint) if v.Backend.JwtSecret != "" { jwtSecret, err := utils.DecryptoFromBase64(v.Backend.JwtSecret, key) diff --git a/internal/model/vendorRecord.go b/internal/model/vendorRecord.go index dc5d936e..022aff5d 100644 --- a/internal/model/vendorRecord.go +++ b/internal/model/vendorRecord.go @@ -17,7 +17,7 @@ type BilibiliVendor struct { Backend string `gorm:"type:varchar(64)"` } -func (b *BilibiliVendor) BeforeSave(tx *gorm.DB) error { +func (b *BilibiliVendor) BeforeSave(_ *gorm.DB) error { key := []byte(b.UserID) for k, v := range b.Cookies { value, err := utils.CryptoToBase64([]byte(v), key) @@ -29,7 +29,7 @@ func (b *BilibiliVendor) BeforeSave(tx *gorm.DB) error { return nil } -func (b *BilibiliVendor) AfterSave(tx *gorm.DB) error { +func (b *BilibiliVendor) AfterSave(_ *gorm.DB) error { key := []byte(b.UserID) for k, v := range b.Cookies { value, err := utils.DecryptoFromBase64(v, key) @@ -62,7 +62,7 @@ func GenAlistServerID(a *AlistVendor) { } } -func (a *AlistVendor) BeforeSave(tx *gorm.DB) error { +func (a *AlistVendor) BeforeSave(_ *gorm.DB) error { key := utils.GenCryptoKey(a.UserID) var err error if a.Host, err = utils.CryptoToBase64([]byte(a.Host), key); err != nil { @@ -77,7 +77,7 @@ func (a *AlistVendor) BeforeSave(tx *gorm.DB) error { return nil } -func (a *AlistVendor) AfterSave(tx *gorm.DB) error { +func (a *AlistVendor) AfterSave(_ *gorm.DB) error { key := utils.GenCryptoKey(a.UserID) host, err := utils.DecryptoFromBase64(a.Host, key) if err != nil { @@ -112,7 +112,7 @@ type EmbyVendor struct { EmbyUserID string `gorm:"type:varchar(32)"` } -func (e *EmbyVendor) BeforeSave(tx *gorm.DB) error { +func (e *EmbyVendor) BeforeSave(_ *gorm.DB) error { key := utils.GenCryptoKey(e.ServerID) var err error if e.Host, err = utils.CryptoToBase64(stream.StringToBytes(e.Host), key); err != nil { @@ -124,7 +124,7 @@ func (e *EmbyVendor) BeforeSave(tx *gorm.DB) error { return nil } -func (e *EmbyVendor) AfterSave(tx *gorm.DB) error { +func (e *EmbyVendor) AfterSave(_ *gorm.DB) error { key := utils.GenCryptoKey(e.ServerID) host, err := utils.DecryptoFromBase64(e.Host, key) if err != nil { diff --git a/internal/op/client.go b/internal/op/client.go index 188431d9..318f707f 100644 --- a/internal/op/client.go +++ b/internal/op/client.go @@ -113,7 +113,7 @@ func (c *Client) NextReader() (int, io.Reader, error) { return c.conn.NextReader() } -func (c *Client) SetStatus(playing bool, seek float64, rate float64, timeDiff float64) error { +func (c *Client) SetStatus(playing bool, seek, rate, timeDiff float64) error { status, err := c.u.SetRoomCurrentStatus(c.r, playing, seek, rate, timeDiff) if err != nil { return err diff --git a/internal/op/current.go b/internal/op/current.go index ef792946..eccdff2c 100644 --- a/internal/op/current.go +++ b/internal/op/current.go @@ -3,6 +3,7 @@ package op import ( "sync" + log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/model" ) @@ -44,7 +45,11 @@ func (c *current) CurrentMovie() model.CurrentMovie { func (c *current) SetMovie(movie model.CurrentMovie, play bool) { c.lock.Lock() defer c.lock.Unlock() - defer db.SetRoomCurrent(c.roomID, &c.current) + defer func() { + if err := db.SetRoomCurrent(c.roomID, &c.current); err != nil { + log.Errorf("set room current failed: %v", err) + } + }() c.current.Movie = movie c.current.SetSeek(0, 0) @@ -61,7 +66,11 @@ func (c *current) Status() model.Status { func (c *current) SetStatus(playing bool, seek, rate, timeDiff float64) *model.Status { c.lock.Lock() defer c.lock.Unlock() - defer db.SetRoomCurrent(c.roomID, &c.current) + defer func() { + if err := db.SetRoomCurrent(c.roomID, &c.current); err != nil { + log.Errorf("set room current failed: %v", err) + } + }() s := c.current.SetStatus(playing, seek, rate, timeDiff) return &s @@ -70,7 +79,11 @@ func (c *current) SetStatus(playing bool, seek, rate, timeDiff float64) *model.S func (c *current) SetSeekRate(seek, rate, timeDiff float64) *model.Status { c.lock.Lock() defer c.lock.Unlock() - defer db.SetRoomCurrent(c.roomID, &c.current) + defer func() { + if err := db.SetRoomCurrent(c.roomID, &c.current); err != nil { + log.Errorf("set room current failed: %v", err) + } + }() s := c.current.SetSeekRate(seek, rate, timeDiff) return &s diff --git a/internal/op/hub.go b/internal/op/hub.go index 4c4b8676..926bdfda 100644 --- a/internal/op/hub.go +++ b/internal/op/hub.go @@ -76,7 +76,7 @@ func (h *Hub) serve() { select { case message := <-h.broadcast: h.devMessage(message.data) - h.clients.Range(func(id string, clients *clients) bool { + h.clients.Range(func(_ string, clients *clients) bool { clients.lock.RLock() defer clients.lock.RUnlock() for _, c := range clients.m { @@ -137,6 +137,7 @@ func (h *Hub) devMessage(msg Message) { switch msg.MessageType() { case websocket.BinaryMessage: log.Debugf("hub: %s, broadcast:\nmessage: %+v", h.id, msg.String()) + default: } } diff --git a/internal/op/message.go b/internal/op/message.go index af0e43a0..6c98dc7d 100644 --- a/internal/op/message.go +++ b/internal/op/message.go @@ -22,6 +22,6 @@ func (pm *PingMessage) String() string { return "Ping" } -func (pm *PingMessage) Encode(w io.Writer) error { +func (pm *PingMessage) Encode(_ io.Writer) error { return nil } diff --git a/internal/op/movie.go b/internal/op/movie.go index 10dd693a..7f074786 100644 --- a/internal/op/movie.go +++ b/internal/op/movie.go @@ -38,14 +38,15 @@ func (m *Movie) SubPath() string { return m.room.SubPath(m.ID) } +//nolint:gosec func (m *Movie) ExpireID(ctx context.Context) (uint64, error) { switch { - case m.Movie.MovieBase.VendorInfo.Vendor == model.VendorAlist: + case m.VendorInfo.Vendor == model.VendorAlist: amcd, _ := m.AlistCache().Raw() if amcd != nil && amcd.Ali != nil { return uint64(amcd.Ali.Last()), nil } - case m.Movie.MovieBase.Live && m.Movie.MovieBase.VendorInfo.Vendor == model.VendorBilibili: + case m.Live && m.VendorInfo.Vendor == model.VendorBilibili: liveCache := m.BilibiliCache().Live _, err := liveCache.Get(ctx) if err != nil { @@ -53,17 +54,18 @@ func (m *Movie) ExpireID(ctx context.Context) (uint64, error) { } return uint64(liveCache.Last()), nil } - return uint64(crc32.ChecksumIEEE([]byte(m.Movie.ID))), nil + return uint64(crc32.ChecksumIEEE([]byte(m.ID))), nil } +//nolint:gosec func (m *Movie) CheckExpired(ctx context.Context, expireID uint64) (bool, error) { switch { - case m.Movie.MovieBase.VendorInfo.Vendor == model.VendorAlist: + case m.VendorInfo.Vendor == model.VendorAlist: amcd, _ := m.AlistCache().Raw() if amcd != nil && amcd.Ali != nil { - return time.Now().UnixNano()-int64(amcd.Ali.Last()) > amcd.Ali.MaxAge(), nil + return time.Now().UnixNano()-amcd.Ali.Last() > amcd.Ali.MaxAge(), nil } - case m.Movie.MovieBase.Live && m.Movie.MovieBase.VendorInfo.Vendor == model.VendorBilibili: + case m.Live && m.VendorInfo.Vendor == model.VendorBilibili: return time.Now().UnixNano()-int64(expireID) > m.BilibiliCache().Live.MaxAge(), nil } id, err := m.ExpireID(ctx) @@ -157,16 +159,16 @@ func (m *Movie) compareAndSwapInitChannel() (*rtmps.Channel, bool) { } func (m *Movie) initChannel() (*rtmps.Channel, error) { - if !m.Movie.MovieBase.Live || (!m.Movie.MovieBase.RtmpSource && !m.Movie.MovieBase.Proxy) { + if !m.Live || (!m.RtmpSource && !m.Proxy) { return nil, errors.New("this movie not support channel") } - if m.Movie.MovieBase.RtmpSource { + if m.RtmpSource { return m.initRtmpSourceChannel() } // Handle proxy case - u, err := url.Parse(m.Movie.MovieBase.URL) + u, err := url.Parse(m.URL) if err != nil { return nil, err } @@ -213,7 +215,7 @@ func (m *Movie) handleRtmpProxy(c *rtmps.Channel) { return } cli := core.NewConnClient() - if err := cli.Start(m.Movie.MovieBase.URL, av.PLAY); err != nil { + if err := cli.Start(m.URL, av.PLAY); err != nil { log.Errorf("push live error: %v", err) cli.Close() time.Sleep(time.Second) @@ -228,7 +230,7 @@ func (m *Movie) handleRtmpProxy(c *rtmps.Channel) { } func (m *Movie) initHTTPProxyChannel() (*rtmps.Channel, error) { - if utils.IsM3u8Url(m.Movie.MovieBase.URL) { + if utils.IsM3u8Url(m.URL) { return nil, errors.New("m3u8 url not support") } @@ -250,13 +252,13 @@ func (m *Movie) handleHTTPProxy(c *rtmps.Channel) { if c.Closed() { return } - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, m.Movie.MovieBase.URL, nil) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, m.URL, nil) if err != nil { log.Errorf("get live error: %v", err) time.Sleep(time.Second) continue } - for k, v := range m.Movie.MovieBase.Headers { + for k, v := range m.Headers { req.Header.Set(k, v) } if req.Header.Get("User-Agent") == "" { @@ -365,18 +367,18 @@ func (m *Movie) validateDirectURL(u *url.URL) error { } func (m *Movie) validateVendorMovie() error { - switch m.Movie.MovieBase.VendorInfo.Vendor { + switch m.VendorInfo.Vendor { case model.VendorBilibili: if m.IsFolder { return errors.New("bilibili folder not support") } - return m.Movie.MovieBase.VendorInfo.Bilibili.Validate() + return m.VendorInfo.Bilibili.Validate() case model.VendorAlist: - return m.Movie.MovieBase.VendorInfo.Alist.Validate() + return m.VendorInfo.Alist.Validate() case model.VendorEmby: - return m.Movie.MovieBase.VendorInfo.Emby.Validate() + return m.VendorInfo.Emby.Validate() default: return errors.New("vendor not implement validate") diff --git a/internal/op/movies.go b/internal/op/movies.go index 5498ee33..f5d4ab12 100644 --- a/internal/op/movies.go +++ b/internal/op/movies.go @@ -18,6 +18,7 @@ type movies struct { cache rwmap.RWMap[string, *Movie] } +//nolint:gosec func (m *movies) AddMovie(mo *model.Movie) error { mo.Position = uint(time.Now().UnixMilli()) movie := &Movie{ @@ -42,6 +43,7 @@ func (m *movies) AddMovie(mo *model.Movie) error { return nil } +//nolint:gosec func (m *movies) AddMovies(mos []*model.Movie) error { inited := make([]*Movie, 0, len(mos)) for _, mo := range mos { @@ -65,7 +67,7 @@ func (m *movies) AddMovies(mos []*model.Movie) error { } for _, mo := range inited { - old, ok := m.cache.Swap(mo.Movie.ID, mo) + old, ok := m.cache.Swap(mo.ID, mo) if ok { _ = old.Close() } @@ -198,14 +200,20 @@ func (m *movies) SwapMoviePositions(id1, id2 string) error { return db.SwapMoviePositions(m.roomID, id1, id2) } -func (m *movies) GetMoviesWithPage(keyword string, page, pageSize int, parentID string) ([]*model.Movie, int64, error) { +func (m *movies) GetMoviesWithPage( + keyword string, + page, pageSize int, + parentID string, +) ([]*model.Movie, int64, error) { scopes := []func(*gorm.DB) *gorm.DB{ db.WithParentMovieID(parentID), } if keyword != "" { scopes = append(scopes, db.WhereMovieNameLikeOrURLLike(keyword, keyword)) } - count, err := db.GetMoviesCountByRoomID(m.roomID, append(scopes, db.Paginate(page, pageSize))...) + count, err := db.GetMoviesCountByRoomID( + m.roomID, + append(scopes, db.Paginate(page, pageSize))...) if err != nil { return nil, 0, err } diff --git a/internal/op/op.go b/internal/op/op.go index ce305554..b3f3f24e 100644 --- a/internal/op/op.go +++ b/internal/op/op.go @@ -7,14 +7,17 @@ import ( "github.com/zijiren233/gencontainer/synccache" ) -func Init(size int) error { - roomCache = synccache.NewSyncCache[string, *Room](time.Minute*5, synccache.WithDeletedCallback[string, *Room](func(v *Room) { - log.WithFields(log.Fields{ - "rid": v.ID, - "rn": v.Name, - }).Debugf("room ttl expired, closing") - v.close() - })) +func Init(_ int) error { + roomCache = synccache.NewSyncCache( + time.Minute*5, + synccache.WithDeletedCallback[string](func(v *Room) { + log.WithFields(log.Fields{ + "rid": v.ID, + "rn": v.Name, + }).Debugf("room ttl expired, closing") + v.close() + }), + ) userCache = synccache.NewSyncCache[string, *User](time.Minute * 5) return nil diff --git a/internal/op/room.go b/internal/op/room.go index a939861e..ed39fcb0 100644 --- a/internal/op/room.go +++ b/internal/op/room.go @@ -273,19 +273,20 @@ func (r *Room) LoadMember(userID string) (*model.RoomMember, error) { } func (r *Room) storeMember(userID string, member *model.RoomMember) *model.RoomMember { - if r.IsCreator(userID) { + switch { + case r.IsCreator(userID): member.Role = model.RoomMemberRoleCreator member.Permissions = model.AllPermissions member.AdminPermissions = model.AllAdminPermissions member.Status = model.RoomMemberStatusActive - } else if r.IsGuest(userID) { + case r.IsGuest(userID): member.Role = model.RoomMemberRoleMember member.Permissions = r.Settings.GuestPermissions member.AdminPermissions = model.NoAdminPermission if member.Status.IsBanned() { member.Status = model.RoomMemberStatusActive } - } else if member.Role.IsAdmin() { + case member.Role.IsAdmin(): member.Permissions = model.AllPermissions } member, _ = r.members.LoadOrStore(userID, member) @@ -325,7 +326,10 @@ func (r *Room) SetPassword(password string) error { var hashedPassword []byte if password != "" { var err error - hashedPassword, err = bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err = bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return err } @@ -443,7 +447,7 @@ func (r *Room) CheckCurrentExpired(ctx context.Context, expireID uint64) (bool, return m.CheckExpired(ctx, expireID) } -func (r *Room) SetCurrentMovie(movieID string, subPath string, play bool) error { +func (r *Room) SetCurrentMovie(movieID, subPath string, play bool) error { currentMovie, err := r.LoadCurrentMovie() if err != nil { if !errors.Is(err, ErrNoCurrentMovie) { @@ -490,7 +494,11 @@ func (r *Room) SwapMoviePositions(id1, id2 string) error { return r.movies.SwapMoviePositions(id1, id2) } -func (r *Room) GetMoviesWithPage(keyword string, page, pageSize int, parentID string) ([]*model.Movie, int64, error) { +func (r *Room) GetMoviesWithPage( + keyword string, + page, pageSize int, + parentID string, +) ([]*model.Movie, int64, error) { return r.movies.GetMoviesWithPage(keyword, page, pageSize, parentID) } @@ -520,11 +528,11 @@ func (r *Room) UserOnlineCount(userID string) int { return r.lazyInitHub().OnlineCount(userID) } -func (r *Room) SetCurrentStatus(playing bool, seek float64, rate float64, timeDiff float64) *model.Status { +func (r *Room) SetCurrentStatus(playing bool, seek, rate, timeDiff float64) *model.Status { return r.current.SetStatus(playing, seek, rate, timeDiff) } -func (r *Room) SetCurrentSeekRate(seek float64, rate float64, timeDiff float64) *model.Status { +func (r *Room) SetCurrentSeekRate(seek, rate, timeDiff float64) *model.Status { return r.current.SetSeekRate(seek, rate, timeDiff) } @@ -587,7 +595,10 @@ func (r *Room) AddMemberPermissions(userID string, permissions model.RoomMemberP return db.AddMemberPermissions(r.ID, userID, permissions) } -func (r *Room) RemoveMemberPermissions(userID string, permissions model.RoomMemberPermission) error { +func (r *Room) RemoveMemberPermissions( + userID string, + permissions model.RoomMemberPermission, +) error { if r.IsGuest(userID) { return r.SetGuestPermissions(r.Settings.GuestPermissions.Remove(permissions)) } @@ -675,7 +686,7 @@ func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPer return errors.New("not admin") } defer r.members.Delete(userID) - return db.RoomSetAdminPermissions(r.ID, userID, permissions) + return db.RoomAddAdminPermissions(r.ID, userID, permissions) } func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdminPermission) error { @@ -691,7 +702,7 @@ func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdmin return errors.New("not admin") } defer r.members.Delete(userID) - return db.RoomSetAdminPermissions(r.ID, userID, 0) + return db.RoomRemoveAdminPermissions(r.ID, userID, permissions) } func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) error { diff --git a/internal/op/rooms.go b/internal/op/rooms.go index 26a62eb5..cea15f6b 100644 --- a/internal/op/rooms.go +++ b/internal/op/rooms.go @@ -14,9 +14,11 @@ import ( var ( roomCache *synccache.SyncCache[string, *Room] ErrRoomCreatorBanned = errors.New("room creator is banned") - ErrRoomCreatorPending = errors.New("room creator is pending approval, please wait for admin to review") - ErrInvalidRoomID = errors.New("invalid room ID: must be 32 characters long") - ErrRoomNotInCache = errors.New("room not found in cache") + ErrRoomCreatorPending = errors.New( + "room creator is pending approval, please wait for admin to review", + ) + ErrInvalidRoomID = errors.New("invalid room ID: must be 32 characters long") + ErrRoomNotInCache = errors.New("room not found in cache") ) type RoomEntry = synccache.Entry[*Room] @@ -25,7 +27,11 @@ func RangeRoomCache(f func(key string, value *RoomEntry) bool) { roomCache.Range(f) } -func CreateRoom(name, password string, maxCount int64, conf ...db.CreateRoomConfig) (*RoomEntry, error) { +func CreateRoom( + name, password string, + maxCount int64, + conf ...db.CreateRoomConfig, +) (*RoomEntry, error) { r, err := db.CreateRoom(name, password, maxCount, conf...) if err != nil { return nil, err diff --git a/internal/op/user.go b/internal/op/user.go index 9de17a17..431b0dcb 100644 --- a/internal/op/user.go +++ b/internal/op/user.go @@ -72,7 +72,10 @@ func (u *User) SetPassword(password string) error { if u.CheckPassword(password) { return errors.New("password is the same") } - hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost) + hashedPassword, err := bcrypt.GenerateFromPassword( + stream.StringToBytes(password), + bcrypt.DefaultCost, + ) if err != nil { return err } @@ -335,7 +338,7 @@ func (u *User) SetRoomSettings(room *Room, setting *model.RoomSettings) error { return room.SetSettings(setting) } -func (u *User) UpdateRoomSettings(room *Room, settings map[string]interface{}) error { +func (u *User) UpdateRoomSettings(room *Room, settings map[string]any) error { if !u.HasRoomAdminPermission(room, model.PermissionSetRoomSettings) { return model.ErrNoPermission } @@ -347,7 +350,7 @@ func (u *User) DeleteRoomMovieByID(room *Room, movieID string) error { if err != nil { return err } - if m.Movie.CreatorID != u.ID && !u.HasRoomPermission(room, model.PermissionDeleteMovie) { + if m.CreatorID != u.ID && !u.HasRoomPermission(room, model.PermissionDeleteMovie) { return model.ErrNoPermission } return room.DeleteMovieByID(movieID) @@ -359,7 +362,7 @@ func (u *User) DeleteRoomMoviesByID(room *Room, movieIDs []string) error { if err != nil { return err } - if m.Movie.CreatorID != u.ID && !u.HasRoomPermission(room, model.PermissionDeleteMovie) { + if m.CreatorID != u.ID && !u.HasRoomPermission(room, model.PermissionDeleteMovie) { return model.ErrNoPermission } } @@ -426,7 +429,7 @@ func (u *User) SwapRoomMoviePositions(room *Room, id1, id2 string) error { }) } -func (u *User) SetRoomCurrentMovie(room *Room, movieID string, subPath string, play bool) error { +func (u *User) SetRoomCurrentMovie(room *Room, movieID, subPath string, play bool) error { if !u.HasRoomPermission(room, model.PermissionSetCurrentMovie) { return model.ErrNoPermission } @@ -502,14 +505,23 @@ func (u *User) VerifyRetrievePasswordCaptchaEmail(e, captcha string) (bool, erro return email.VerifyRetrievePasswordCaptchaEmail(u.ID, e, captcha) } -func (u *User) GetRoomMoviesWithPage(room *Room, keyword string, page, pageSize int, parentID string) ([]*model.Movie, int64, error) { +func (u *User) GetRoomMoviesWithPage( + room *Room, + keyword string, + page, pageSize int, + parentID string, +) ([]*model.Movie, int64, error) { if !u.HasRoomPermission(room, model.PermissionGetMovieList) { return nil, 0, model.ErrNoPermission } return room.GetMoviesWithPage(keyword, page, pageSize, parentID) } -func (u *User) SetRoomCurrentStatus(room *Room, playing bool, seek, rate, timeDiff float64) (*model.Status, error) { +func (u *User) SetRoomCurrentStatus( + room *Room, + playing bool, + seek, rate, timeDiff float64, +) (*model.Status, error) { if !u.HasRoomPermission(room, model.PermissionSetCurrentStatus) { return nil, model.ErrNoPermission } @@ -546,7 +558,11 @@ func (u *User) DeleteRoomMember(room *Room, userID string) error { return room.DeleteMember(userID) } -func (u *User) SetMemberPermissions(room *Room, userID string, permissions model.RoomMemberPermission) error { +func (u *User) SetMemberPermissions( + room *Room, + userID string, + permissions model.RoomMemberPermission, +) error { if !u.HasRoomAdminPermission(room, model.PermissionSetUserPermission) { return model.ErrNoPermission } @@ -566,7 +582,11 @@ func (u *User) SetMemberPermissions(room *Room, userID string, permissions model }) } -func (u *User) AddMemberPermissions(room *Room, userID string, permissions model.RoomMemberPermission) error { +func (u *User) AddMemberPermissions( + room *Room, + userID string, + permissions model.RoomMemberPermission, +) error { if !u.HasRoomAdminPermission(room, model.PermissionSetUserPermission) { return model.ErrNoPermission } @@ -586,7 +606,11 @@ func (u *User) AddMemberPermissions(room *Room, userID string, permissions model }) } -func (u *User) RemoveMemberPermissions(room *Room, userID string, permissions model.RoomMemberPermission) error { +func (u *User) RemoveMemberPermissions( + room *Room, + userID string, + permissions model.RoomMemberPermission, +) error { if !u.HasRoomAdminPermission(room, model.PermissionSetUserPermission) { return model.ErrNoPermission } @@ -633,7 +657,11 @@ func (u *User) ApproveRoomPendingMember(room *Room, userID string) error { return room.ApprovePendingMember(userID) } -func (u *User) SetRoomAdmin(room *Room, userID string, permissions model.RoomAdminPermission) error { +func (u *User) SetRoomAdmin( + room *Room, + userID string, + permissions model.RoomAdminPermission, +) error { if !u.IsRoomCreator(room) { return model.ErrNoPermission } @@ -650,7 +678,11 @@ func (u *User) SetRoomAdmin(room *Room, userID string, permissions model.RoomAdm }) } -func (u *User) SetRoomMember(room *Room, userID string, permissions model.RoomMemberPermission) error { +func (u *User) SetRoomMember( + room *Room, + userID string, + permissions model.RoomMemberPermission, +) error { if !u.IsRoomCreator(room) { return model.ErrNoPermission } @@ -667,7 +699,11 @@ func (u *User) SetRoomMember(room *Room, userID string, permissions model.RoomMe }) } -func (u *User) SetRoomAdminPermissions(room *Room, userID string, permissions model.RoomAdminPermission) error { +func (u *User) SetRoomAdminPermissions( + room *Room, + userID string, + permissions model.RoomAdminPermission, +) error { if !u.IsRoomCreator(room) { return model.ErrNoPermission } @@ -684,7 +720,11 @@ func (u *User) SetRoomAdminPermissions(room *Room, userID string, permissions mo }) } -func (u *User) AddRoomAdminPermissions(room *Room, userID string, permissions model.RoomAdminPermission) error { +func (u *User) AddRoomAdminPermissions( + room *Room, + userID string, + permissions model.RoomAdminPermission, +) error { if !u.IsRoomCreator(room) { return model.ErrNoPermission } @@ -701,7 +741,11 @@ func (u *User) AddRoomAdminPermissions(room *Room, userID string, permissions mo }) } -func (u *User) RemoveRoomAdminPermissions(room *Room, userID string, permissions model.RoomAdminPermission) error { +func (u *User) RemoveRoomAdminPermissions( + room *Room, + userID string, + permissions model.RoomAdminPermission, +) error { if !u.IsRoomCreator(room) { return model.ErrNoPermission } diff --git a/internal/op/users.go b/internal/op/users.go index 28dd0b0d..9bffefd1 100644 --- a/internal/op/users.go +++ b/internal/op/users.go @@ -17,7 +17,9 @@ type UserEntry = synccache.Entry[*User] var ( ErrUserBanned = errors.New("user account has been banned") - ErrUserPending = errors.New("user account is pending approval, please wait for administrator review") + ErrUserPending = errors.New( + "user account is pending approval, please wait for administrator review", + ) ) func LoadOrInitUser(u *model.User) (*UserEntry, error) { @@ -61,7 +63,7 @@ func LoadOrInitUserByUsername(username string) (*UserEntry, error) { return LoadOrInitUser(u) } -func CreateUser(username string, password string, conf ...db.CreateUserConfig) (*UserEntry, error) { +func CreateUser(username, password string, conf ...db.CreateUserConfig) (*UserEntry, error) { if username == "" { return nil, errors.New("username cannot be empty") } @@ -73,7 +75,12 @@ func CreateUser(username string, password string, conf ...db.CreateUserConfig) ( return LoadOrInitUser(u) } -func CreateOrLoadUserWithProvider(username, password string, p provider.OAuth2Provider, pid string, conf ...db.CreateUserConfig) (*UserEntry, error) { +func CreateOrLoadUserWithProvider( + username, password string, + p provider.OAuth2Provider, + pid string, + conf ...db.CreateUserConfig, +) (*UserEntry, error) { u, err := db.CreateOrLoadUserWithProvider(username, password, p, pid, conf...) if err != nil { return nil, err @@ -82,7 +89,10 @@ func CreateOrLoadUserWithProvider(username, password string, p provider.OAuth2Pr return LoadOrInitUser(u) } -func CreateUserWithEmail(username, password, email string, conf ...db.CreateUserConfig) (*UserEntry, error) { +func CreateUserWithEmail( + username, password, email string, + conf ...db.CreateUserConfig, +) (*UserEntry, error) { u, err := db.CreateUserWithEmail(username, password, email, conf...) if err != nil { return nil, err @@ -125,7 +135,7 @@ func DeleteUserByID(id string) error { func CloseUserByID(id string) error { userCache.Delete(id) - roomCache.Range(func(key string, value *synccache.Entry[*Room]) bool { + roomCache.Range(func(_ string, value *synccache.Entry[*Room]) bool { if value.Value().CreatorID == id { CompareAndCloseRoom(value) } @@ -138,7 +148,7 @@ func CompareAndCloseUser(user *UserEntry) error { if !userCache.CompareAndDelete(user.Value().ID, user) { return nil } - roomCache.Range(func(key string, value *synccache.Entry[*Room]) bool { + roomCache.Range(func(_ string, value *synccache.Entry[*Room]) bool { if value.Value().CreatorID == user.Value().ID { CompareAndCloseRoom(value) } diff --git a/internal/provider/aggregation.go b/internal/provider/aggregation.go index b8f24c2b..81f45e09 100644 --- a/internal/provider/aggregation.go +++ b/internal/provider/aggregation.go @@ -1,12 +1,15 @@ package provider type AggregationProviderInterface interface { - ExtractProvider(OAuth2Provider) (Interface, error) + ExtractProvider(provider OAuth2Provider) (Interface, error) Provider() OAuth2Provider Providers() []OAuth2Provider } -func ExtractProviders(p AggregationProviderInterface, providers ...OAuth2Provider) ([]Interface, error) { +func ExtractProviders( + p AggregationProviderInterface, + providers ...OAuth2Provider, +) ([]Interface, error) { if len(providers) == 0 { providers = p.Providers() } diff --git a/internal/provider/aggregations/rainbow.go b/internal/provider/aggregations/rainbow.go index 3cb362d0..8fd19ac2 100644 --- a/internal/provider/aggregations/rainbow.go +++ b/internal/provider/aggregations/rainbow.go @@ -109,7 +109,10 @@ type rainbowNewAuthURLResp struct { ErrCode int `json:"errcode"` } -func (p *rainbowGenericProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *rainbowGenericProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { result, err := url.JoinPath(p.parent.api, "/connect.php") if err != nil { return nil, err diff --git a/internal/provider/plugins/client.go b/internal/provider/plugins/client.go index d47c5f45..2814afe3 100644 --- a/internal/provider/plugins/client.go +++ b/internal/provider/plugins/client.go @@ -25,7 +25,7 @@ func (c *GRPCClient) Provider() provider.OAuth2Provider { if err != nil { return "" } - return resp.Name + return resp.GetName() } func (c *GRPCClient) NewAuthURL(ctx context.Context, state string) (string, error) { @@ -33,7 +33,7 @@ func (c *GRPCClient) NewAuthURL(ctx context.Context, state string) (string, erro if err != nil { return "", err } - return resp.Url, nil + return resp.GetUrl(), nil } func (c *GRPCClient) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { @@ -44,7 +44,7 @@ func (c *GRPCClient) GetUserInfo(ctx context.Context, code string) (*provider.Us return nil, err } return &provider.UserInfo{ - Username: resp.Username, - ProviderUserID: resp.ProviderUserId, + Username: resp.GetUsername(), + ProviderUserID: resp.GetProviderUserId(), }, nil } diff --git a/internal/provider/plugins/example/example_authing/example_authing.go b/internal/provider/plugins/example/example_authing/example_authing.go index f77e5b47..17da4d92 100644 --- a/internal/provider/plugins/example/example_authing/example_authing.go +++ b/internal/provider/plugins/example/example_authing/example_authing.go @@ -14,9 +14,11 @@ import ( ) // Linux/Mac/Windows: -// CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./internal/provider/plugins/example/example_authing/example_authing.go -// CGO_ENABLED=0 GOOS=dawin GOARCH=amd64 go build ./internal/provider/plugins/example/example_authing/example_authing.go -// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build ./internal/provider/plugins/example/example_authing/example_authing.go +// CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build +// ./internal/provider/plugins/example/example_authing/example_authing.go CGO_ENABLED=0 GOOS=dawin +// GOARCH=amd64 go build ./internal/provider/plugins/example/example_authing/example_authing.go +// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build +// ./internal/provider/plugins/example/example_authing/example_authing.go // // mv gitee {data-dir}/plugins/oauth2/authing // @@ -36,7 +38,10 @@ func newAuthingProvider(authURL string) provider.Interface { config: oauth2.Config{ Scopes: []string{"profile"}, Endpoint: oauth2.Endpoint{ - AuthURL: fmt.Sprintf("https://%s.authing.cn/oauth/auth", authURL), // 授权码(authorization_code)获取接口 + AuthURL: fmt.Sprintf( + "https://%s.authing.cn/oauth/auth", + authURL, + ), // 授权码(authorization_code)获取接口 TokenURL: fmt.Sprintf("https://%s.authing.cn/oauth/token", authURL), // Token端点 }, }, @@ -53,17 +58,25 @@ func (p *AuthingProvider) Provider() provider.OAuth2Provider { return "authing" // 插件名 } -func (p *AuthingProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *AuthingProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } -func (p *AuthingProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *AuthingProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.config.Exchange(ctx, code) if err != nil { return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://core.authing.cn/oauth/me", nil) // 身份端点 + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://core.authing.cn/oauth/me", + nil, + ) // 身份端点 if err != nil { return nil, err } diff --git a/internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go b/internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go index 43168a1e..b4cdea7a 100644 --- a/internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go +++ b/internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go @@ -14,9 +14,12 @@ import ( ) // Linux/Mac/Windows: -// CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go -// CGO_ENABLED=0 GOOS=dawin GOARCH=amd64 go build ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go -// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go +// CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build +// ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go CGO_ENABLED=0 +// GOOS=dawin GOARCH=amd64 go build +// ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go CGO_ENABLED=0 +// GOOS=windows GOARCH=amd64 go build +// ./internal/provider/plugins/example/example_feishu-sso/example_feishu-sso.go // // mv gitee {data-dir}/plugins/oauth2/feishu-sso // @@ -37,8 +40,14 @@ func newFeishuSSOProvider(ssoid string) provider.Interface { config: oauth2.Config{ Scopes: []string{"profile"}, Endpoint: oauth2.Endpoint{ - AuthURL: fmt.Sprintf("https://anycross.feishu.cn/sso/%s/oauth2/auth", ssoid), // 授权码(authorization_code)获取接口 - TokenURL: fmt.Sprintf("https://anycross.feishu.cn/sso/%s/oauth2/token", ssoid), // 获取访问令牌(access_token) + AuthURL: fmt.Sprintf( + "https://anycross.feishu.cn/sso/%s/oauth2/auth", + ssoid, + ), // 授权码(authorization_code)获取接口 + TokenURL: fmt.Sprintf( + "https://anycross.feishu.cn/sso/%s/oauth2/token", + ssoid, + ), // 获取访问令牌(access_token) }, }, ssoid: ssoid, @@ -55,7 +64,7 @@ func (p *FeishuSSOProvider) Provider() provider.OAuth2Provider { return "feishu-sso" // 插件名 } -func (p *FeishuSSOProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *FeishuSSOProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -67,13 +76,21 @@ func (p *FeishuSSOProvider) RefreshToken(ctx context.Context, tk string) (*oauth return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: tk}).Token() } -func (p *FeishuSSOProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *FeishuSSOProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.GetToken(ctx, code) if err != nil { return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://anycross.feishu.cn/sso/%s/oauth2/userinfo", p.ssoid), nil) // 身份端点 + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("https://anycross.feishu.cn/sso/%s/oauth2/userinfo", p.ssoid), + nil, + ) // 身份端点 if err != nil { return nil, err } diff --git a/internal/provider/plugins/example/example_gitee/example_gitee.go b/internal/provider/plugins/example/example_gitee/example_gitee.go index 19e8b7f1..c52292b1 100644 --- a/internal/provider/plugins/example/example_gitee/example_gitee.go +++ b/internal/provider/plugins/example/example_gitee/example_gitee.go @@ -46,7 +46,7 @@ func (p *GiteeProvider) Provider() provider.OAuth2Provider { return "gitee" } -func (p *GiteeProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *GiteeProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -64,7 +64,12 @@ func (p *GiteeProvider) GetUserInfo(ctx context.Context, code string) (*provider return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitee.com/api/v5/user", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://gitee.com/api/v5/user", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/plugins/plugin.go b/internal/provider/plugins/plugin.go index 3caf5f49..a026a6f2 100644 --- a/internal/provider/plugins/plugin.go +++ b/internal/provider/plugins/plugin.go @@ -16,10 +16,13 @@ import ( func InitProviderPlugins(name string, arg []string, logger hclog.Logger) error { client := NewProviderPlugin(name, arg, logger) - err := sysnotify.RegisterSysNotifyTask(0, sysnotify.NewSysNotifyTask("plugin", sysnotify.NotifyTypeEXIT, func() error { - client.Kill() - return nil - })) + err := sysnotify.RegisterSysNotifyTask( + 0, + sysnotify.NewSysNotifyTask("plugin", sysnotify.NotifyTypeEXIT, func() error { + client.Kill() + return nil + }), + ) if err != nil { return err } @@ -54,12 +57,16 @@ type ProviderPlugin struct { Impl provider.Interface } -func (p *ProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { +func (p *ProviderPlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error { providerpb.RegisterOauth2PluginServer(s, &GRPCServer{Impl: p.Impl}) return nil } -func (p *ProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { +func (p *ProviderPlugin) GRPCClient( + _ context.Context, + _ *plugin.GRPCBroker, + c *grpc.ClientConn, +) (any, error) { return &GRPCClient{client: providerpb.NewOauth2PluginClient(c)}, nil } diff --git a/internal/provider/plugins/server.go b/internal/provider/plugins/server.go index 068419e3..0c4d8d27 100644 --- a/internal/provider/plugins/server.go +++ b/internal/provider/plugins/server.go @@ -12,30 +12,39 @@ type GRPCServer struct { Impl provider.Interface } -func (s *GRPCServer) Init(ctx context.Context, req *providerpb.InitReq) (*providerpb.Enpty, error) { +func (s *GRPCServer) Init(_ context.Context, req *providerpb.InitReq) (*providerpb.Enpty, error) { opt := provider.Oauth2Option{ - ClientID: req.ClientId, - ClientSecret: req.ClientSecret, - RedirectURL: req.RedirectUrl, + ClientID: req.GetClientId(), + ClientSecret: req.GetClientSecret(), + RedirectURL: req.GetRedirectUrl(), } s.Impl.Init(opt) return &providerpb.Enpty{}, nil } -func (s *GRPCServer) Provider(ctx context.Context, req *providerpb.Enpty) (*providerpb.ProviderResp, error) { +func (s *GRPCServer) Provider( + _ context.Context, + _ *providerpb.Enpty, +) (*providerpb.ProviderResp, error) { return &providerpb.ProviderResp{Name: s.Impl.Provider()}, nil } -func (s *GRPCServer) NewAuthURL(ctx context.Context, req *providerpb.NewAuthURLReq) (*providerpb.NewAuthURLResp, error) { - s2, err := s.Impl.NewAuthURL(ctx, req.State) +func (s *GRPCServer) NewAuthURL( + ctx context.Context, + req *providerpb.NewAuthURLReq, +) (*providerpb.NewAuthURLResp, error) { + s2, err := s.Impl.NewAuthURL(ctx, req.GetState()) if err != nil { return nil, err } return &providerpb.NewAuthURLResp{Url: s2}, nil } -func (s *GRPCServer) GetUserInfo(ctx context.Context, req *providerpb.GetUserInfoReq) (*providerpb.GetUserInfoResp, error) { - userInfo, err := s.Impl.GetUserInfo(ctx, req.Code) +func (s *GRPCServer) GetUserInfo( + ctx context.Context, + req *providerpb.GetUserInfoReq, +) (*providerpb.GetUserInfoResp, error) { + userInfo, err := s.Impl.GetUserInfo(ctx, req.GetCode()) if err != nil { return nil, err } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 8b3f6a46..24ba0d13 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -18,16 +18,16 @@ type Oauth2Option struct { } type Provider interface { - Init(Oauth2Option) + Init(opt Oauth2Option) Provider() OAuth2Provider } -type ProviderRegistSetting interface { +type RegistSetting interface { RegistSetting(group string) } type Interface interface { Provider - NewAuthURL(context.Context, string) (string, error) - GetUserInfo(context.Context, string) (*UserInfo, error) + NewAuthURL(ctx context.Context, state string) (string, error) + GetUserInfo(ctx context.Context, code string) (*UserInfo, error) } diff --git a/internal/provider/providers/baidu-netdisk.go b/internal/provider/providers/baidu-netdisk.go index 8c8cc415..5d3df262 100644 --- a/internal/provider/providers/baidu-netdisk.go +++ b/internal/provider/providers/baidu-netdisk.go @@ -38,7 +38,7 @@ func (p *BaiduNetDiskProvider) Provider() provider.OAuth2Provider { return "baidu-netdisk" } -func (p *BaiduNetDiskProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *BaiduNetDiskProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -50,13 +50,21 @@ func (p *BaiduNetDiskProvider) RefreshToken(ctx context.Context, tk string) (*oa return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: tk}).Token() } -func (p *BaiduNetDiskProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *BaiduNetDiskProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.GetToken(ctx, code) if err != nil { return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo&access_token="+tk.AccessToken, nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo&access_token="+tk.AccessToken, + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/baidu.go b/internal/provider/providers/baidu.go index 52569be2..e34408ac 100644 --- a/internal/provider/providers/baidu.go +++ b/internal/provider/providers/baidu.go @@ -36,7 +36,7 @@ func (p *BaiduProvider) Provider() provider.OAuth2Provider { return "baidu" } -func (p *BaiduProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *BaiduProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -54,7 +54,12 @@ func (p *BaiduProvider) GetUserInfo(ctx context.Context, code string) (*provider return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token="+tk.AccessToken, nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token="+tk.AccessToken, + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/casdoor.go b/internal/provider/providers/casdoor.go index 56d9396f..66d98b95 100644 --- a/internal/provider/providers/casdoor.go +++ b/internal/provider/providers/casdoor.go @@ -32,7 +32,7 @@ func (p *casdoorProvider) Init(opt provider.Oauth2Option) { p.config.RedirectURL = opt.RedirectURL } -func (p *casdoorProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *casdoorProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -44,7 +44,10 @@ func (p *casdoorProvider) RefreshToken(ctx context.Context, token string) (*oaut return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: token}).Token() } -func (p *casdoorProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *casdoorProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.GetToken(ctx, code) if err != nil { return nil, err @@ -85,21 +88,21 @@ type casdoorUserInfo struct { func (p *casdoorProvider) RegistSetting(group string) { settings.NewStringSetting( group+"_endpoint", "", group, - settings.WithAfterInitString(func(ss settings.StringSetting, s string) { + settings.WithAfterInitString(func(_ settings.StringSetting, s string) { p.endpoint = s p.config.Endpoint = oauth2.Endpoint{ AuthURL: s + "/login/oauth/authorize", TokenURL: s + "/api/login/oauth/access_token", } }), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { u, err := url.Parse(s) if err != nil { return "", err } return fmt.Sprintf("%s://%s", u.Scheme, u.Host), nil }), - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, s string) { p.endpoint = s p.config.Endpoint = oauth2.Endpoint{ AuthURL: s + "/login/oauth/authorize", diff --git a/internal/provider/providers/discord.go b/internal/provider/providers/discord.go index 90a36b2e..a84fb75f 100644 --- a/internal/provider/providers/discord.go +++ b/internal/provider/providers/discord.go @@ -35,7 +35,7 @@ func (p *DiscordProvider) Provider() provider.OAuth2Provider { return "discord" } -func (p *DiscordProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *DiscordProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -47,13 +47,21 @@ func (p *DiscordProvider) RefreshToken(ctx context.Context, tk string) (*oauth2. return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: tk}).Token() } -func (p *DiscordProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *DiscordProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.config.Exchange(ctx, code) if err != nil { return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://discord.com/api/v10/oauth2/@me", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://discord.com/api/v10/oauth2/@me", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/gitee.go b/internal/provider/providers/gitee.go index 91b790e4..8d6dbddb 100644 --- a/internal/provider/providers/gitee.go +++ b/internal/provider/providers/gitee.go @@ -36,7 +36,7 @@ func (p *GiteeProvider) Provider() provider.OAuth2Provider { return "gitee" } -func (p *GiteeProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *GiteeProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -54,7 +54,12 @@ func (p *GiteeProvider) GetUserInfo(ctx context.Context, code string) (*provider return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitee.com/api/v5/user", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://gitee.com/api/v5/user", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/github.go b/internal/provider/providers/github.go index 77feca1f..e5fa10aa 100644 --- a/internal/provider/providers/github.go +++ b/internal/provider/providers/github.go @@ -34,7 +34,7 @@ func (p *GithubProvider) Provider() provider.OAuth2Provider { return "github" } -func (p *GithubProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *GithubProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } diff --git a/internal/provider/providers/gitlab.go b/internal/provider/providers/gitlab.go index 11f3ec4c..7b281ebf 100644 --- a/internal/provider/providers/gitlab.go +++ b/internal/provider/providers/gitlab.go @@ -32,7 +32,7 @@ func (g *GitlabProvider) Provider() provider.OAuth2Provider { return "gitlab" } -func (g *GitlabProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (g *GitlabProvider) NewAuthURL(_ context.Context, state string) (string, error) { return g.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -50,7 +50,12 @@ func (g *GitlabProvider) GetUserInfo(ctx context.Context, code string) (*provide return nil, err } client := g.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitlab.com/api/v4/user", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://gitlab.com/api/v4/user", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/google.go b/internal/provider/providers/google.go index ecce1535..e494c89c 100644 --- a/internal/provider/providers/google.go +++ b/internal/provider/providers/google.go @@ -33,7 +33,7 @@ func (g *GoogleProvider) Provider() provider.OAuth2Provider { return "google" } -func (g *GoogleProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (g *GoogleProvider) NewAuthURL(_ context.Context, state string) (string, error) { return g.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -51,7 +51,12 @@ func (g *GoogleProvider) GetUserInfo(ctx context.Context, code string) (*provide return nil, err } client := g.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://www.googleapis.com/oauth2/v2/userinfo", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/logto.go b/internal/provider/providers/logto.go index e74230bc..57341384 100644 --- a/internal/provider/providers/logto.go +++ b/internal/provider/providers/logto.go @@ -32,7 +32,7 @@ func (p *logtoProvider) Init(opt provider.Oauth2Option) { p.config.RedirectURL = opt.RedirectURL } -func (p *logtoProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *logtoProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -86,21 +86,21 @@ type logtoUserInfo struct { func (p *logtoProvider) RegistSetting(group string) { settings.NewStringSetting( group+"_endpoint", "", group, - settings.WithAfterInitString(func(ss settings.StringSetting, s string) { + settings.WithAfterInitString(func(_ settings.StringSetting, s string) { p.endpoint = s p.config.Endpoint = oauth2.Endpoint{ AuthURL: s + "/oidc/auth", TokenURL: s + "/oidc/token", } }), - settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { + settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) { u, err := url.Parse(s) if err != nil { return "", err } return fmt.Sprintf("%s://%s", u.Scheme, u.Host), nil }), - settings.WithAfterSetString(func(ss settings.StringSetting, s string) { + settings.WithAfterSetString(func(_ settings.StringSetting, s string) { p.endpoint = s p.config.Endpoint = oauth2.Endpoint{ AuthURL: s + "/oidc/auth", diff --git a/internal/provider/providers/microsoft.go b/internal/provider/providers/microsoft.go index 9db65e1a..f3a9932f 100644 --- a/internal/provider/providers/microsoft.go +++ b/internal/provider/providers/microsoft.go @@ -33,7 +33,7 @@ func (p *MicrosoftProvider) Provider() provider.OAuth2Provider { return "microsoft" } -func (p *MicrosoftProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *MicrosoftProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -45,13 +45,21 @@ func (p *MicrosoftProvider) RefreshToken(ctx context.Context, tk string) (*oauth return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: tk}).Token() } -func (p *MicrosoftProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) { +func (p *MicrosoftProvider) GetUserInfo( + ctx context.Context, + code string, +) (*provider.UserInfo, error) { tk, err := p.GetToken(ctx, code) if err != nil { return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://graph.microsoft.com/v1.0/me", nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + "https://graph.microsoft.com/v1.0/me", + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/qq.go b/internal/provider/providers/qq.go index bfaf640c..28c884d9 100644 --- a/internal/provider/providers/qq.go +++ b/internal/provider/providers/qq.go @@ -38,7 +38,7 @@ func (p *QQProvider) Provider() provider.OAuth2Provider { return "qq" } -func (p *QQProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *QQProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -50,7 +50,12 @@ func (p *QQProvider) GetToken(ctx context.Context, code string) (*oauth2.Token, params.Set("client_id", p.config.ClientID) params.Set("client_secret", p.config.ClientSecret) params.Set("fmt", "json") - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s", p.config.Endpoint.TokenURL, params.Encode()), nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("%s?%s", p.config.Endpoint.TokenURL, params.Encode()), + nil, + ) if err != nil { return nil, err } @@ -70,7 +75,12 @@ func (p *QQProvider) RefreshToken(ctx context.Context, tk string) (*oauth2.Token params.Set("client_id", p.config.ClientID) params.Set("client_secret", p.config.ClientSecret) params.Set("fmt", "json") - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s", p.config.Endpoint.TokenURL, params.Encode()), nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("%s?%s", p.config.Endpoint.TokenURL, params.Encode()), + nil, + ) if err != nil { return nil, err } @@ -88,7 +98,12 @@ func (p *QQProvider) GetUserInfo(ctx context.Context, code string) (*provider.Us if err != nil { return nil, err } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://graph.qq.com/oauth2.0/me?access_token=%s&fmt=json", tk.AccessToken), nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("https://graph.qq.com/oauth2.0/me?access_token=%s&fmt=json", tk.AccessToken), + nil, + ) if err != nil { return nil, err } @@ -102,7 +117,17 @@ func (p *QQProvider) GetUserInfo(ctx context.Context, code string) (*provider.Us if err != nil { return nil, err } - req, err = http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s&fmt=json", tk.AccessToken, p.config.ClientID, ume.Openid), nil) + req, err = http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf( + "https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s&fmt=json", + tk.AccessToken, + p.config.ClientID, + ume.Openid, + ), + nil, + ) if err != nil { return nil, err } diff --git a/internal/provider/providers/xiaomi.go b/internal/provider/providers/xiaomi.go index 05076e45..7e2ae7ae 100644 --- a/internal/provider/providers/xiaomi.go +++ b/internal/provider/providers/xiaomi.go @@ -36,7 +36,7 @@ func (p *XiaomiProvider) Provider() provider.OAuth2Provider { return "xiaomi" } -func (p *XiaomiProvider) NewAuthURL(ctx context.Context, state string) (string, error) { +func (p *XiaomiProvider) NewAuthURL(_ context.Context, state string) (string, error) { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil } @@ -54,7 +54,16 @@ func (p *XiaomiProvider) GetUserInfo(ctx context.Context, code string) (*provide return nil, err } client := p.config.Client(ctx, tk) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://open.account.xiaomi.com/user/profile?clientId=%s&token=%s", p.config.ClientID, tk.AccessToken), nil) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf( + "https://open.account.xiaomi.com/user/profile?clientId=%s&token=%s", + p.config.ClientID, + tk.AccessToken, + ), + nil, + ) if err != nil { return nil, err } diff --git a/internal/rtmp/rtmp.go b/internal/rtmp/rtmp.go index d7984efa..84122c45 100644 --- a/internal/rtmp/rtmp.go +++ b/internal/rtmp/rtmp.go @@ -19,9 +19,13 @@ type Claims struct { } func AuthRtmpPublish(authorization string) (movieID string, err error) { - t, err := jwt.ParseWithClaims(strings.TrimPrefix(authorization, `Bearer `), &Claims{}, func(token *jwt.Token) (any, error) { - return stream.StringToBytes(conf.Conf.Jwt.Secret), nil - }) + t, err := jwt.ParseWithClaims( + strings.TrimPrefix(authorization, `Bearer `), + &Claims{}, + func(_ *jwt.Token) (any, error) { + return stream.StringToBytes(conf.Conf.Jwt.Secret), nil + }, + ) if err != nil { return "", errors.New("auth failed") } @@ -39,7 +43,8 @@ func NewRtmpAuthorization(movieID string) (string, error) { NotBefore: jwt.NewNumericDate(time.Now()), }, } - return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) + return jwt.NewWithClaims(jwt.SigningMethodHS256, claims). + SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) } func Init(rs *rtmps.Server) { diff --git a/internal/settings/bool.go b/internal/settings/bool.go index 50092b67..74c79dc3 100644 --- a/internal/settings/bool.go +++ b/internal/settings/bool.go @@ -11,14 +11,14 @@ import ( type BoolSetting interface { Setting - Set(bool) error + Set(v bool) error Get() bool Default() bool - Parse(string) (bool, error) - Stringify(bool) string - SetBeforeInit(func(BoolSetting, bool) (bool, error)) - SetBeforeSet(func(BoolSetting, bool) (bool, error)) - SetAfterGet(func(BoolSetting, bool) bool) + Parse(value string) (bool, error) + Stringify(value bool) string + SetBeforeInit(beforeInit func(BoolSetting, bool) (bool, error)) + SetBeforeSet(beforeSet func(BoolSetting, bool) (bool, error)) + SetAfterGet(afterGet func(BoolSetting, bool) bool) } var _ BoolSetting = (*Bool)(nil) @@ -72,7 +72,12 @@ func WithAfterGetBool(afterGet func(BoolSetting, bool) bool) BoolSettingOption { } } -func newBool(name string, value bool, group model.SettingGroup, options ...BoolSettingOption) *Bool { +func newBool( + name string, + value bool, + group model.SettingGroup, + options ...BoolSettingOption, +) *Bool { b := &Bool{ setting: setting{ name: name, @@ -225,7 +230,12 @@ func (b *Bool) Interface() any { return b.Get() } -func NewBoolSetting(k string, v bool, g model.SettingGroup, options ...BoolSettingOption) BoolSetting { +func NewBoolSetting( + k string, + v bool, + g model.SettingGroup, + options ...BoolSettingOption, +) BoolSetting { _, loaded := Settings[k] if loaded { panic(fmt.Sprintf("setting %s already exists", k)) @@ -233,7 +243,12 @@ func NewBoolSetting(k string, v bool, g model.SettingGroup, options ...BoolSetti return CoverBoolSetting(k, v, g, options...) } -func CoverBoolSetting(k string, v bool, g model.SettingGroup, options ...BoolSettingOption) BoolSetting { +func CoverBoolSetting( + k string, + v bool, + g model.SettingGroup, + options ...BoolSettingOption, +) BoolSetting { b := newBool(k, v, g, options...) Settings[k] = b if GroupSettings[g] == nil { @@ -253,7 +268,12 @@ func LoadBoolSetting(k string) (BoolSetting, bool) { return b, ok } -func LoadOrNewBoolSetting(k string, v bool, g model.SettingGroup, options ...BoolSettingOption) BoolSetting { +func LoadOrNewBoolSetting( + k string, + v bool, + g model.SettingGroup, + options ...BoolSettingOption, +) BoolSetting { if s, ok := LoadBoolSetting(k); ok { return s } diff --git a/internal/settings/floate64.go b/internal/settings/floate64.go index 6e0b68b3..15f036ce 100644 --- a/internal/settings/floate64.go +++ b/internal/settings/floate64.go @@ -12,14 +12,14 @@ import ( type Float64Setting interface { Setting - Set(float64) error + Set(v float64) error Get() float64 Default() float64 - Parse(string) (float64, error) - Stringify(float64) string - SetBeforeInit(func(Float64Setting, float64) (float64, error)) - SetBeforeSet(func(Float64Setting, float64) (float64, error)) - SetAfterGet(func(Float64Setting, float64) float64) + Parse(value string) (float64, error) + Stringify(value float64) string + SetBeforeInit(beforeInit func(Float64Setting, float64) (float64, error)) + SetBeforeSet(beforeSet func(Float64Setting, float64) (float64, error)) + SetAfterGet(afterGet func(Float64Setting, float64) float64) } var _ Float64Setting = (*Float64)(nil) @@ -50,13 +50,17 @@ func WithValidatorFloat64(validator func(float64) error) Float64SettingOption { } } -func WithBeforeInitFloat64(beforeInit func(Float64Setting, float64) (float64, error)) Float64SettingOption { +func WithBeforeInitFloat64( + beforeInit func(Float64Setting, float64) (float64, error), +) Float64SettingOption { return func(s *Float64) { s.SetBeforeInit(beforeInit) } } -func WithBeforeSetFloat64(beforeSet func(Float64Setting, float64) (float64, error)) Float64SettingOption { +func WithBeforeSetFloat64( + beforeSet func(Float64Setting, float64) (float64, error), +) Float64SettingOption { return func(s *Float64) { s.SetBeforeSet(beforeSet) } @@ -80,7 +84,12 @@ func WithAfterGetFloat64(afterGet func(Float64Setting, float64) float64) Float64 } } -func newFloat64(name string, value float64, group model.SettingGroup, options ...Float64SettingOption) *Float64 { +func newFloat64( + name string, + value float64, + group model.SettingGroup, + options ...Float64SettingOption, +) *Float64 { f := &Float64{ setting: setting{ name: name, @@ -247,7 +256,12 @@ func (f *Float64) Interface() any { return f.Get() } -func NewFloat64Setting(k string, v float64, g model.SettingGroup, options ...Float64SettingOption) Float64Setting { +func NewFloat64Setting( + k string, + v float64, + g model.SettingGroup, + options ...Float64SettingOption, +) Float64Setting { _, loaded := Settings[k] if loaded { panic(fmt.Sprintf("setting %s already exists", k)) @@ -255,7 +269,12 @@ func NewFloat64Setting(k string, v float64, g model.SettingGroup, options ...Flo return CoverFloat64Setting(k, v, g, options...) } -func CoverFloat64Setting(k string, v float64, g model.SettingGroup, options ...Float64SettingOption) Float64Setting { +func CoverFloat64Setting( + k string, + v float64, + g model.SettingGroup, + options ...Float64SettingOption, +) Float64Setting { f := newFloat64(k, v, g, options...) Settings[k] = f if GroupSettings[g] == nil { @@ -275,7 +294,12 @@ func LoadFloat64Setting(k string) (Float64Setting, bool) { return f, ok } -func LoadOrNewFloat64Setting(k string, v float64, g model.SettingGroup, options ...Float64SettingOption) Float64Setting { +func LoadOrNewFloat64Setting( + k string, + v float64, + g model.SettingGroup, + options ...Float64SettingOption, +) Float64Setting { s, ok := LoadFloat64Setting(k) if ok { return s diff --git a/internal/settings/int64.go b/internal/settings/int64.go index efc0d8f2..2c129a41 100644 --- a/internal/settings/int64.go +++ b/internal/settings/int64.go @@ -11,14 +11,14 @@ import ( type Int64Setting interface { Setting - Set(int64) error + Set(v int64) error Get() int64 Default() int64 - Parse(string) (int64, error) - Stringify(int64) string - SetBeforeInit(func(Int64Setting, int64) (int64, error)) - SetBeforeSet(func(Int64Setting, int64) (int64, error)) - SetAfterGet(func(Int64Setting, int64) int64) + Parse(value string) (int64, error) + Stringify(value int64) string + SetBeforeInit(beforeInit func(Int64Setting, int64) (int64, error)) + SetBeforeSet(beforeSet func(Int64Setting, int64) (int64, error)) + SetAfterGet(afterGet func(Int64Setting, int64) int64) } var _ Int64Setting = (*Int64)(nil) @@ -79,7 +79,12 @@ func WithAfterGetInt64(afterGet func(Int64Setting, int64) int64) Int64SettingOpt } } -func newInt64(name string, value int64, group model.SettingGroup, options ...Int64SettingOption) *Int64 { +func newInt64( + name string, + value int64, + group model.SettingGroup, + options ...Int64SettingOption, +) *Int64 { i := &Int64{ setting: setting{ name: name, @@ -246,7 +251,12 @@ func (i *Int64) Interface() any { return i.Get() } -func NewInt64Setting(k string, v int64, g model.SettingGroup, options ...Int64SettingOption) Int64Setting { +func NewInt64Setting( + k string, + v int64, + g model.SettingGroup, + options ...Int64SettingOption, +) Int64Setting { _, loaded := Settings[k] if loaded { panic(fmt.Sprintf("setting %s already exists", k)) @@ -254,7 +264,12 @@ func NewInt64Setting(k string, v int64, g model.SettingGroup, options ...Int64Se return CoverInt64Setting(k, v, g, options...) } -func CoverInt64Setting(k string, v int64, g model.SettingGroup, options ...Int64SettingOption) Int64Setting { +func CoverInt64Setting( + k string, + v int64, + g model.SettingGroup, + options ...Int64SettingOption, +) Int64Setting { i := newInt64(k, v, g, options...) Settings[k] = i if GroupSettings[g] == nil { @@ -274,7 +289,12 @@ func LoadInt64Setting(k string) (Int64Setting, bool) { return i, ok } -func LoadOrNewInt64Setting(k string, v int64, g model.SettingGroup, options ...Int64SettingOption) Int64Setting { +func LoadOrNewInt64Setting( + k string, + v int64, + g model.SettingGroup, + options ...Int64SettingOption, +) Int64Setting { s, ok := LoadInt64Setting(k) if ok { return s diff --git a/internal/settings/setting.go b/internal/settings/setting.go index 9630f172..dd4bbc8c 100644 --- a/internal/settings/setting.go +++ b/internal/settings/setting.go @@ -56,7 +56,7 @@ func pushNeedInit(s Setting) { panic("push need init failed, setting is nil") } for i, item := range needInit.items { - if item.Setting.Name() == s.Name() { + if item.Name() == s.Name() { heap.Remove(needInit, i) break } @@ -87,17 +87,18 @@ type Setting interface { Name() string Type() model.SettingType Group() model.SettingGroup - Init(string) error + Init(value string) error Inited() bool - SetInitPriority(int) + SetInitPriority(priority int) InitPriority() int String() string - SetString(string) error + SetString(value string) error DefaultString() string DefaultInterface() any Interface() any } +//nolint:errcheck func SetValue(name string, value any) error { s, ok := Settings[name] if !ok { diff --git a/internal/settings/string.go b/internal/settings/string.go index 9f81c70a..7015956f 100644 --- a/internal/settings/string.go +++ b/internal/settings/string.go @@ -10,14 +10,14 @@ import ( type StringSetting interface { Setting - Set(string) error + Set(v string) error Get() string Default() string - Parse(string) (string, error) - Stringify(string) string - SetBeforeInit(func(StringSetting, string) (string, error)) - SetBeforeSet(func(StringSetting, string) (string, error)) - SetAfterGet(func(StringSetting, string) string) + Parse(value string) (string, error) + Stringify(value string) string + SetBeforeInit(beforeInit func(StringSetting, string) (string, error)) + SetBeforeSet(beforeSet func(StringSetting, string) (string, error)) + SetAfterGet(afterGet func(StringSetting, string) string) } var _ StringSetting = (*String)(nil) @@ -49,13 +49,17 @@ func WithValidatorString(validator func(string) error) StringSettingOption { } } -func WithBeforeInitString(beforeInit func(StringSetting, string) (string, error)) StringSettingOption { +func WithBeforeInitString( + beforeInit func(StringSetting, string) (string, error), +) StringSettingOption { return func(s *String) { s.SetBeforeInit(beforeInit) } } -func WithBeforeSetString(beforeSet func(StringSetting, string) (string, error)) StringSettingOption { +func WithBeforeSetString( + beforeSet func(StringSetting, string) (string, error), +) StringSettingOption { return func(s *String) { s.SetBeforeSet(beforeSet) } @@ -79,7 +83,11 @@ func WithAfterGetString(afterGet func(StringSetting, string) string) StringSetti } } -func newString(name string, value string, group model.SettingGroup, options ...StringSettingOption) *String { +func newString( + name, value string, + group model.SettingGroup, + options ...StringSettingOption, +) *String { s := &String{ setting: setting{ name: name, @@ -246,7 +254,11 @@ func (s *String) Interface() any { return s.Get() } -func NewStringSetting(k string, v string, g model.SettingGroup, options ...StringSettingOption) StringSetting { +func NewStringSetting( + k, v string, + g model.SettingGroup, + options ...StringSettingOption, +) StringSetting { _, loaded := Settings[k] if loaded { panic(fmt.Sprintf("setting %s already exists", k)) @@ -254,7 +266,11 @@ func NewStringSetting(k string, v string, g model.SettingGroup, options ...Strin return CoverStringSetting(k, v, g, options...) } -func CoverStringSetting(k string, v string, g model.SettingGroup, options ...StringSettingOption) StringSetting { +func CoverStringSetting( + k, v string, + g model.SettingGroup, + options ...StringSettingOption, +) StringSetting { s := newString(k, v, g, options...) Settings[k] = s if GroupSettings[g] == nil { @@ -274,7 +290,11 @@ func LoadStringSetting(k string) (StringSetting, bool) { return ss, ok } -func LoadOrNewStringSetting(k string, v string, g model.SettingGroup, options ...StringSettingOption) StringSetting { +func LoadOrNewStringSetting( + k, v string, + g model.SettingGroup, + options ...StringSettingOption, +) StringSetting { s, ok := LoadStringSetting(k) if ok { return s diff --git a/internal/settings/var.go b/internal/settings/var.go index 3a5a7840..e4d1fea1 100644 --- a/internal/settings/var.go +++ b/internal/settings/var.go @@ -15,12 +15,17 @@ var ( RoomMustNoNeedPwd BoolSetting CreateRoomNeedReview = NewBoolSetting("create_room_need_review", false, model.SettingGroupRoom) // default 48 hours - RoomTTL = NewInt64Setting("room_ttl", 48, model.SettingGroupRoom, WithBeforeSetInt64(func(is Int64Setting, i int64) (int64, error) { - if i < 1 { - return 0, errors.New("room ttl must be greater than 0") - } - return i, nil - })) + RoomTTL = NewInt64Setting( + "room_ttl", + 48, + model.SettingGroupRoom, + WithBeforeSetInt64(func(_ Int64Setting, i int64) (int64, error) { + if i < 1 { + return 0, errors.New("room ttl must be greater than 0") + } + return i, nil + }), + ) ) func init() { @@ -28,9 +33,11 @@ func init() { "room_must_need_pwd", false, model.SettingGroupRoom, - WithBeforeSetBool(func(bs BoolSetting, b bool) (bool, error) { + WithBeforeSetBool(func(_ BoolSetting, b bool) (bool, error) { if b && RoomMustNoNeedPwd.Get() { - return false, errors.New("room_must_need_pwd and room_must_no_need_pwd can't be true at the same time") + return false, errors.New( + "room_must_need_pwd and room_must_no_need_pwd can't be true at the same time", + ) } return b, nil }), @@ -39,9 +46,11 @@ func init() { "room_must_no_need_pwd", false, model.SettingGroupRoom, - WithBeforeSetBool(func(bs BoolSetting, b bool) (bool, error) { + WithBeforeSetBool(func(_ BoolSetting, b bool) (bool, error) { if b && RoomMustNeedPwd.Get() { - return false, errors.New("room_must_need_pwd and room_must_no_need_pwd can't be true at the same time") + return false, errors.New( + "room_must_need_pwd and room_must_no_need_pwd can't be true at the same time", + ) } return b, nil }), @@ -49,12 +58,20 @@ func init() { } var ( - DisableUserSignup = NewBoolSetting("disable_user_signup", false, model.SettingGroupUser) - SignupNeedReview = NewBoolSetting("signup_need_review", false, model.SettingGroupUser) - EnablePasswordSignup = NewBoolSetting("enable_password_signup", false, model.SettingGroupUser) - PasswordSignupNeedReview = NewBoolSetting("password_signup_need_review", false, model.SettingGroupUser) - UserMaxRoomCount = NewInt64Setting("user_max_room_count", 3, model.SettingGroupUser) - EnableGuest = NewBoolSetting("enable_guest", true, model.SettingGroupUser) + DisableUserSignup = NewBoolSetting("disable_user_signup", false, model.SettingGroupUser) + SignupNeedReview = NewBoolSetting("signup_need_review", false, model.SettingGroupUser) + EnablePasswordSignup = NewBoolSetting( + "enable_password_signup", + false, + model.SettingGroupUser, + ) + PasswordSignupNeedReview = NewBoolSetting( + "password_signup_need_review", + false, + model.SettingGroupUser, + ) + UserMaxRoomCount = NewInt64Setting("user_max_room_count", 3, model.SettingGroupUser) + EnableGuest = NewBoolSetting("enable_guest", true, model.SettingGroupUser) ) var ( @@ -73,9 +90,14 @@ var ( TSDisguisedAsPng = NewBoolSetting("ts_disguised_as_png", true, model.SettingGroupRtmp) ) -var DatabaseVersion = NewStringSetting("database_version", db.CurrentVersion, model.SettingGroupDatabase, WithBeforeSetString(func(ss StringSetting, s string) (string, error) { - return "", errors.New("not support change database version") -})) +var DatabaseVersion = NewStringSetting( + "database_version", + db.CurrentVersion, + model.SettingGroupDatabase, + WithBeforeSetString(func(_ StringSetting, _ string) (string, error) { + return "", errors.New("not support change database version") + }), +) var HOST = NewStringSetting( "host", diff --git a/internal/sysNotify/signal.go b/internal/sysNotify/signal.go index 610c5a3f..17ddb9ed 100644 --- a/internal/sysNotify/signal.go +++ b/internal/sysNotify/signal.go @@ -11,7 +11,15 @@ import ( func (sn *SysNotify) Init() { sn.c = make(chan os.Signal, 1) - signal.Notify(sn.c, syscall.SIGHUP /*1*/, syscall.SIGINT /*2*/, syscall.SIGQUIT /*3*/, syscall.SIGTERM /*15*/, syscall.SIGUSR1 /*10*/, syscall.SIGUSR2 /*12*/) + signal.Notify( + sn.c, + syscall.SIGHUP, /*1*/ + syscall.SIGINT, /*2*/ + syscall.SIGQUIT, /*3*/ + syscall.SIGTERM, /*15*/ + syscall.SIGUSR1, /*10*/ + syscall.SIGUSR2, /*12*/ + ) } func parseSysNotifyType(s os.Signal) NotifyType { diff --git a/internal/sysNotify/sysNotify.go b/internal/sysNotify/sysNotify.go index 52ef2eb9..cf6e5e2b 100644 --- a/internal/sysNotify/sysNotify.go +++ b/internal/sysNotify/sysNotify.go @@ -6,7 +6,6 @@ import ( "sync" log "github.com/sirupsen/logrus" - "github.com/zijiren233/gencontainer/pqueue" "github.com/zijiren233/gencontainer/rwmap" ) diff --git a/internal/sysnotify/signal.go b/internal/sysnotify/signal.go index 610c5a3f..17ddb9ed 100644 --- a/internal/sysnotify/signal.go +++ b/internal/sysnotify/signal.go @@ -11,7 +11,15 @@ import ( func (sn *SysNotify) Init() { sn.c = make(chan os.Signal, 1) - signal.Notify(sn.c, syscall.SIGHUP /*1*/, syscall.SIGINT /*2*/, syscall.SIGQUIT /*3*/, syscall.SIGTERM /*15*/, syscall.SIGUSR1 /*10*/, syscall.SIGUSR2 /*12*/) + signal.Notify( + sn.c, + syscall.SIGHUP, /*1*/ + syscall.SIGINT, /*2*/ + syscall.SIGQUIT, /*3*/ + syscall.SIGTERM, /*15*/ + syscall.SIGUSR1, /*10*/ + syscall.SIGUSR2, /*12*/ + ) } func parseSysNotifyType(s os.Signal) NotifyType { diff --git a/internal/sysnotify/sysnotify.go b/internal/sysnotify/sysnotify.go index 52ef2eb9..cf6e5e2b 100644 --- a/internal/sysnotify/sysnotify.go +++ b/internal/sysnotify/sysnotify.go @@ -6,7 +6,6 @@ import ( "sync" log "github.com/sirupsen/logrus" - "github.com/zijiren233/gencontainer/pqueue" "github.com/zijiren233/gencontainer/rwmap" ) diff --git a/internal/vendor/alist.go b/internal/vendor/alist.go index 0b83232b..bf5b6929 100644 --- a/internal/vendor/alist.go +++ b/internal/vendor/alist.go @@ -4,10 +4,9 @@ import ( "context" "errors" - "google.golang.org/grpc" - "github.com/synctv-org/vendors/api/alist" alistService "github.com/synctv-org/vendors/service/alist" + "google.golang.org/grpc" ) type AlistInterface = alist.AlistHTTPServer @@ -19,9 +18,7 @@ func LoadAlistClient(name string) AlistInterface { return alistLocalClient } -var ( - alistLocalClient AlistInterface -) +var alistLocalClient AlistInterface func init() { alistLocalClient = alistService.NewAlistService(nil) @@ -59,7 +56,10 @@ func (a *grpcAlist) FsList(ctx context.Context, req *alist.FsListReq) (*alist.Fs return a.client.FsList(ctx, req) } -func (a *grpcAlist) FsOther(ctx context.Context, req *alist.FsOtherReq) (*alist.FsOtherResp, error) { +func (a *grpcAlist) FsOther( + ctx context.Context, + req *alist.FsOtherReq, +) (*alist.FsOtherResp, error) { return a.client.FsOther(ctx, req) } @@ -71,6 +71,9 @@ func (a *grpcAlist) Me(ctx context.Context, req *alist.MeReq) (*alist.MeResp, er return a.client.Me(ctx, req) } -func (a *grpcAlist) FsSearch(ctx context.Context, req *alist.FsSearchReq) (*alist.FsSearchResp, error) { +func (a *grpcAlist) FsSearch( + ctx context.Context, + req *alist.FsSearchReq, +) (*alist.FsSearchResp, error) { return a.client.FsSearch(ctx, req) } diff --git a/internal/vendor/bilibili.go b/internal/vendor/bilibili.go index 3f5aab52..f5663b2d 100644 --- a/internal/vendor/bilibili.go +++ b/internal/vendor/bilibili.go @@ -4,10 +4,9 @@ import ( "context" "errors" - "google.golang.org/grpc" - "github.com/synctv-org/vendors/api/bilibili" bilibiliService "github.com/synctv-org/vendors/service/bilibili" + "google.golang.org/grpc" ) type BilibiliInterface = bilibili.BilibiliHTTPServer @@ -48,70 +47,121 @@ func newGrpcBilibili(client bilibili.BilibiliClient) BilibiliInterface { } } -func (g *grpcBilibili) GetLiveDanmuInfo(ctx context.Context, in *bilibili.GetLiveDanmuInfoReq) (*bilibili.GetLiveDanmuInfoResp, error) { +func (g *grpcBilibili) GetLiveDanmuInfo( + ctx context.Context, + in *bilibili.GetLiveDanmuInfoReq, +) (*bilibili.GetLiveDanmuInfoResp, error) { return g.client.GetLiveDanmuInfo(ctx, in) } -func (g *grpcBilibili) NewQRCode(ctx context.Context, in *bilibili.Empty) (*bilibili.NewQRCodeResp, error) { +func (g *grpcBilibili) NewQRCode( + ctx context.Context, + in *bilibili.Empty, +) (*bilibili.NewQRCodeResp, error) { return g.client.NewQRCode(ctx, in) } -func (g *grpcBilibili) LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error) { +func (g *grpcBilibili) LoginWithQRCode( + ctx context.Context, + in *bilibili.LoginWithQRCodeReq, +) (*bilibili.LoginWithQRCodeResp, error) { return g.client.LoginWithQRCode(ctx, in) } -func (g *grpcBilibili) NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error) { +func (g *grpcBilibili) NewCaptcha( + ctx context.Context, + in *bilibili.Empty, +) (*bilibili.NewCaptchaResp, error) { return g.client.NewCaptcha(ctx, in) } -func (g *grpcBilibili) NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error) { +func (g *grpcBilibili) NewSMS( + ctx context.Context, + in *bilibili.NewSMSReq, +) (*bilibili.NewSMSResp, error) { return g.client.NewSMS(ctx, in) } -func (g *grpcBilibili) LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error) { +func (g *grpcBilibili) LoginWithSMS( + ctx context.Context, + in *bilibili.LoginWithSMSReq, +) (*bilibili.LoginWithSMSResp, error) { return g.client.LoginWithSMS(ctx, in) } -func (g *grpcBilibili) ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error) { +func (g *grpcBilibili) ParseVideoPage( + ctx context.Context, + in *bilibili.ParseVideoPageReq, +) (*bilibili.VideoPageInfo, error) { return g.client.ParseVideoPage(ctx, in) } -func (g *grpcBilibili) GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error) { +func (g *grpcBilibili) GetVideoURL( + ctx context.Context, + in *bilibili.GetVideoURLReq, +) (*bilibili.VideoURL, error) { return g.client.GetVideoURL(ctx, in) } -func (g *grpcBilibili) GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error) { +func (g *grpcBilibili) GetDashVideoURL( + ctx context.Context, + in *bilibili.GetDashVideoURLReq, +) (*bilibili.GetDashVideoURLResp, error) { return g.client.GetDashVideoURL(ctx, in) } -func (g *grpcBilibili) GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error) { +func (g *grpcBilibili) GetSubtitles( + ctx context.Context, + in *bilibili.GetSubtitlesReq, +) (*bilibili.GetSubtitlesResp, error) { return g.client.GetSubtitles(ctx, in) } -func (g *grpcBilibili) ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error) { +func (g *grpcBilibili) ParsePGCPage( + ctx context.Context, + in *bilibili.ParsePGCPageReq, +) (*bilibili.VideoPageInfo, error) { return g.client.ParsePGCPage(ctx, in) } -func (g *grpcBilibili) GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error) { +func (g *grpcBilibili) GetPGCURL( + ctx context.Context, + in *bilibili.GetPGCURLReq, +) (*bilibili.VideoURL, error) { return g.client.GetPGCURL(ctx, in) } -func (g *grpcBilibili) GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error) { +func (g *grpcBilibili) GetDashPGCURL( + ctx context.Context, + in *bilibili.GetDashPGCURLReq, +) (*bilibili.GetDashPGCURLResp, error) { return g.client.GetDashPGCURL(ctx, in) } -func (g *grpcBilibili) UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error) { +func (g *grpcBilibili) UserInfo( + ctx context.Context, + in *bilibili.UserInfoReq, +) (*bilibili.UserInfoResp, error) { return g.client.UserInfo(ctx, in) } -func (g *grpcBilibili) Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error) { +func (g *grpcBilibili) Match( + ctx context.Context, + in *bilibili.MatchReq, +) (*bilibili.MatchResp, error) { return g.client.Match(ctx, in) } -func (g *grpcBilibili) GetLiveStreams(ctx context.Context, in *bilibili.GetLiveStreamsReq) (*bilibili.GetLiveStreamsResp, error) { +func (g *grpcBilibili) GetLiveStreams( + ctx context.Context, + in *bilibili.GetLiveStreamsReq, +) (*bilibili.GetLiveStreamsResp, error) { return g.client.GetLiveStreams(ctx, in) } -func (g *grpcBilibili) ParseLivePage(ctx context.Context, req *bilibili.ParseLivePageReq) (*bilibili.VideoPageInfo, error) { +func (g *grpcBilibili) ParseLivePage( + ctx context.Context, + req *bilibili.ParseLivePageReq, +) (*bilibili.VideoPageInfo, error) { return g.client.ParseLivePage(ctx, req) } diff --git a/internal/vendor/emby.go b/internal/vendor/emby.go index 9e4c21f2..8ff05132 100644 --- a/internal/vendor/emby.go +++ b/internal/vendor/emby.go @@ -4,10 +4,9 @@ import ( "context" "errors" - "google.golang.org/grpc" - "github.com/synctv-org/vendors/api/emby" embyService "github.com/synctv-org/vendors/service/emby" + "google.golang.org/grpc" ) type EmbyInterface = emby.EmbyHTTPServer @@ -19,9 +18,7 @@ func LoadEmbyClient(name string) EmbyInterface { return embyLocalClient } -var ( - embyLocalClient EmbyInterface -) +var embyLocalClient EmbyInterface func init() { embyLocalClient = embyService.NewEmbyService(nil) @@ -59,11 +56,17 @@ func (e *grpcEmby) GetItem(ctx context.Context, req *emby.GetItemReq) (*emby.Ite return e.client.GetItem(ctx, req) } -func (e *grpcEmby) GetItems(ctx context.Context, req *emby.GetItemsReq) (*emby.GetItemsResp, error) { +func (e *grpcEmby) GetItems( + ctx context.Context, + req *emby.GetItemsReq, +) (*emby.GetItemsResp, error) { return e.client.GetItems(ctx, req) } -func (e *grpcEmby) GetSystemInfo(ctx context.Context, req *emby.SystemInfoReq) (*emby.SystemInfoResp, error) { +func (e *grpcEmby) GetSystemInfo( + ctx context.Context, + req *emby.SystemInfoReq, +) (*emby.SystemInfoResp, error) { return e.client.GetSystemInfo(ctx, req) } @@ -79,10 +82,16 @@ func (e *grpcEmby) Me(ctx context.Context, req *emby.MeReq) (*emby.MeResp, error return e.client.Me(ctx, req) } -func (e *grpcEmby) PlaybackInfo(ctx context.Context, req *emby.PlaybackInfoReq) (*emby.PlaybackInfoResp, error) { +func (e *grpcEmby) PlaybackInfo( + ctx context.Context, + req *emby.PlaybackInfoReq, +) (*emby.PlaybackInfoResp, error) { return e.client.PlaybackInfo(ctx, req) } -func (e *grpcEmby) DeleteActiveEncodeings(ctx context.Context, req *emby.DeleteActiveEncodeingsReq) (*emby.Empty, error) { +func (e *grpcEmby) DeleteActiveEncodeings( + ctx context.Context, + req *emby.DeleteActiveEncodeingsReq, +) (*emby.Empty, error) { return e.client.DeleteActiveEncodeings(ctx, req) } diff --git a/internal/vendor/vendor.go b/internal/vendor/vendor.go index 835a5047..1176c9eb 100644 --- a/internal/vendor/vendor.go +++ b/internal/vendor/vendor.go @@ -42,7 +42,7 @@ func init() { type Backends struct { conns map[string]*BackendConn - clients *VendorClients + clients *Clients } var ( @@ -50,21 +50,17 @@ var ( lock sync.Mutex ) -func LoadClients() *VendorClients { +func LoadClients() *Clients { return backends.Load().clients } -func storeBackends(conns map[string]*BackendConn, clients *VendorClients) { +func storeBackends(conns map[string]*BackendConn, clients *Clients) { backends.Store(&Backends{ conns: conns, clients: clients, }) } -func loadBackends() *Backends { - return backends.Load() -} - func LoadConns() map[string]*BackendConn { return backends.Load().conns } @@ -86,7 +82,7 @@ func Init(ctx context.Context) error { return nil } -func EnableVendorBackend(ctx context.Context, endpoint string) (err error) { +func EnableVendorBackend(_ context.Context, endpoint string) (err error) { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -121,7 +117,7 @@ func EnableVendorBackend(ctx context.Context, endpoint string) (err error) { return nil } -func EnableVendorBackends(ctx context.Context, endpoints []string) (err error) { +func EnableVendorBackends(_ context.Context, endpoints []string) (err error) { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -164,7 +160,7 @@ func EnableVendorBackends(ctx context.Context, endpoints []string) (err error) { return nil } -func DisableVendorBackend(ctx context.Context, endpoint string) (err error) { +func DisableVendorBackend(_ context.Context, endpoint string) (err error) { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -199,7 +195,7 @@ func DisableVendorBackend(ctx context.Context, endpoint string) (err error) { return nil } -func DisableVendorBackends(ctx context.Context, endpoints []string) (err error) { +func DisableVendorBackends(_ context.Context, endpoints []string) (err error) { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -278,7 +274,7 @@ func AddVendorBackend(ctx context.Context, backend *model.VendorBackend) error { return nil } -func DeleteVendorBackend(ctx context.Context, endpoint string) error { +func DeleteVendorBackend(_ context.Context, endpoint string) error { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -309,7 +305,7 @@ func DeleteVendorBackend(ctx context.Context, endpoint string) error { return nil } -func DeleteVendorBackends(ctx context.Context, endpoints []string) error { +func DeleteVendorBackends(_ context.Context, endpoints []string) error { if !lock.TryLock() { return errors.New("vendor backend is updating") } @@ -319,11 +315,11 @@ func DeleteVendorBackends(ctx context.Context, endpoints []string) error { beforeConn := make([]*grpc.ClientConn, len(endpoints)) for i, endpoint := range endpoints { - if conn, ok := m[endpoint]; !ok { + conn, ok := m[endpoint] + if !ok { return fmt.Errorf("endpoint not found: %s", endpoint) - } else { - beforeConn[i] = conn.Conn } + beforeConn[i] = conn.Conn delete(m, endpoint) } @@ -388,25 +384,28 @@ type BackendConn struct { Info *model.VendorBackend } -type VendorClients struct { +type Clients struct { bilibili map[string]BilibiliInterface alist map[string]AlistInterface emby map[string]EmbyInterface } -func (b *VendorClients) BilibiliClients() map[string]BilibiliInterface { +func (b *Clients) BilibiliClients() map[string]BilibiliInterface { return b.bilibili } -func (b *VendorClients) AlistClients() map[string]AlistInterface { +func (b *Clients) AlistClients() map[string]AlistInterface { return b.alist } -func (b *VendorClients) EmbyClients() map[string]EmbyInterface { +func (b *Clients) EmbyClients() map[string]EmbyInterface { return b.emby } -func newBackendConn(ctx context.Context, conf *model.VendorBackend) (conns *BackendConn, err error) { +func newBackendConn( + ctx context.Context, + conf *model.VendorBackend, +) (conns *BackendConn, err error) { cc, err := NewGrpcConn(ctx, &conf.Backend) if err != nil { return conns, err @@ -417,7 +416,10 @@ func newBackendConn(ctx context.Context, conf *model.VendorBackend) (conns *Back }, nil } -func newBackendConns(ctx context.Context, conf []*model.VendorBackend) (conns map[string]*BackendConn, err error) { +func newBackendConns( + ctx context.Context, + conf []*model.VendorBackend, +) (conns map[string]*BackendConn, err error) { conns = make(map[string]*BackendConn, len(conf)) defer func() { if err != nil { @@ -441,8 +443,8 @@ func newBackendConns(ctx context.Context, conf []*model.VendorBackend) (conns ma return conns, nil } -func newVendorClients(conns map[string]*BackendConn) (*VendorClients, error) { - clients := &VendorClients{ +func newVendorClients(conns map[string]*BackendConn) (*Clients, error) { + clients := &Clients{ bilibili: make(map[string]BilibiliInterface), alist: make(map[string]AlistInterface), emby: make(map[string]EmbyInterface), @@ -453,7 +455,10 @@ func newVendorClients(conns map[string]*BackendConn) (*VendorClients, error) { } if conn.Info.UsedBy.Bilibili { if _, ok := clients.bilibili[conn.Info.UsedBy.BilibiliBackendName]; ok { - return nil, fmt.Errorf("duplicate bilibili backend name: %s", conn.Info.UsedBy.BilibiliBackendName) + return nil, fmt.Errorf( + "duplicate bilibili backend name: %s", + conn.Info.UsedBy.BilibiliBackendName, + ) } cli, err := NewBilibiliGrpcClient(conn.Conn) if err != nil { @@ -463,7 +468,10 @@ func newVendorClients(conns map[string]*BackendConn) (*VendorClients, error) { } if conn.Info.UsedBy.Alist { if _, ok := clients.alist[conn.Info.UsedBy.AlistBackendName]; ok { - return nil, fmt.Errorf("duplicate alist backend name: %s", conn.Info.UsedBy.AlistBackendName) + return nil, fmt.Errorf( + "duplicate alist backend name: %s", + conn.Info.UsedBy.AlistBackendName, + ) } cli, err := NewAlistGrpcClient(conn.Conn) if err != nil { @@ -473,7 +481,10 @@ func newVendorClients(conns map[string]*BackendConn) (*VendorClients, error) { } if conn.Info.UsedBy.Emby { if _, ok := clients.emby[conn.Info.UsedBy.EmbyBackendName]; ok { - return nil, fmt.Errorf("duplicate emby backend name: %s", conn.Info.UsedBy.EmbyBackendName) + return nil, fmt.Errorf( + "duplicate emby backend name: %s", + conn.Info.UsedBy.EmbyBackendName, + ) } cli, err := NewEmbyGrpcClient(conn.Conn) if err != nil { @@ -501,16 +512,20 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er conf.Endpoint += ":80" } } - middlewares := []middleware.Middleware{kcircuitbreaker.Client(kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker { - return sre.NewBreaker( - sre.WithRequest(25), - sre.WithWindow(time.Second*15), - ) - }))} + middlewares := []middleware.Middleware{ + kcircuitbreaker.Client( + kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker { + return sre.NewBreaker( + sre.WithRequest(25), + sre.WithWindow(time.Second*15), + ) + }), + ), + } if conf.JwtSecret != "" { key := []byte(conf.JwtSecret) - middlewares = append(middlewares, jwt.Client(func(token *jwtv5.Token) (interface{}, error) { + middlewares = append(middlewares, jwt.Client(func(_ *jwtv5.Token) (any, error) { return key, nil }, jwt.WithSigningMethod(jwtv5.SigningMethodHS256))) } @@ -528,7 +543,8 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er opts = append(opts, ggrpc.WithTimeout(timeout)) } - if conf.Consul.ServiceName != "" { + switch { + case conf.Consul.ServiceName != "": c := api.DefaultConfig() c.Address = conf.Endpoint c.Token = conf.Consul.Token @@ -539,12 +555,12 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er if err != nil { return nil, err } - endpoint := fmt.Sprintf("discovery:///%s", conf.Consul.ServiceName) + endpoint := "discovery:///" + conf.Consul.ServiceName dis := consul.New(client) opts = append(opts, ggrpc.WithEndpoint(endpoint), ggrpc.WithDiscovery(dis)) log.Infof("new grpc client with consul: %s", conf.Endpoint) - } else if conf.Etcd.ServiceName != "" { - endpoint := fmt.Sprintf("discovery:///%s", conf.Etcd.ServiceName) + case conf.Etcd.ServiceName != "": + endpoint := "discovery:///" + conf.Etcd.ServiceName cli, err := clientv3.New(clientv3.Config{ Endpoints: []string{conf.Endpoint}, Username: conf.Etcd.Username, @@ -556,7 +572,7 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er dis := etcd.New(cli) opts = append(opts, ggrpc.WithEndpoint(endpoint), ggrpc.WithDiscovery(dis)) log.Infof("new grpc client with etcd: %v", conf.Endpoint) - } else { + default: opts = append(opts, ggrpc.WithEndpoint(conf.Endpoint)) log.Infof("new grpc client with endpoint: %s", conf.Endpoint) } @@ -572,7 +588,8 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er rootCAs.AppendCertsFromPEM([]byte(conf.CustomCa)) } opts = append(opts, ggrpc.WithTLSConfig(&tls.Config{ - RootCAs: rootCAs, + RootCAs: rootCAs, + MinVersion: tls.VersionTLS12, })) con, err = ggrpc.Dial( @@ -591,7 +608,7 @@ func NewGrpcConn(ctx context.Context, conf *model.Backend) (*grpc.ClientConn, er return con, nil } -func NewHttpClientConn(ctx context.Context, conf *model.Backend) (*http.Client, error) { +func NewHTTPClientConn(ctx context.Context, conf *model.Backend) (*http.Client, error) { if err := conf.Validate(); err != nil { return nil, err } @@ -606,16 +623,20 @@ func NewHttpClientConn(ctx context.Context, conf *model.Backend) (*http.Client, conf.Endpoint += ":80" } } - middlewares := []middleware.Middleware{kcircuitbreaker.Client(kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker { - return sre.NewBreaker( - sre.WithRequest(25), - sre.WithWindow(time.Second*15), - ) - }))} + middlewares := []middleware.Middleware{ + kcircuitbreaker.Client( + kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker { + return sre.NewBreaker( + sre.WithRequest(25), + sre.WithWindow(time.Second*15), + ) + }), + ), + } if conf.JwtSecret != "" { key := []byte(conf.JwtSecret) - middlewares = append(middlewares, jwt.Client(func(token *jwtv5.Token) (interface{}, error) { + middlewares = append(middlewares, jwt.Client(func(_ *jwtv5.Token) (any, error) { return key, nil }, jwt.WithSigningMethod(jwtv5.SigningMethodHS256))) } @@ -647,11 +668,13 @@ func NewHttpClientConn(ctx context.Context, conf *model.Backend) (*http.Client, rootCAs.AppendCertsFromPEM(b) } opts = append(opts, http.WithTLSConfig(&tls.Config{ - RootCAs: rootCAs, + RootCAs: rootCAs, + MinVersion: tls.VersionTLS12, })) } - if conf.Consul.ServiceName != "" { + switch { + case conf.Consul.ServiceName != "": c := api.DefaultConfig() c.Address = conf.Endpoint c.Token = conf.Consul.Token @@ -662,12 +685,12 @@ func NewHttpClientConn(ctx context.Context, conf *model.Backend) (*http.Client, if err != nil { return nil, err } - endpoint := fmt.Sprintf("discovery:///%s", conf.Consul.ServiceName) + endpoint := "discovery:///" + conf.Consul.ServiceName dis := consul.New(client) opts = append(opts, http.WithEndpoint(endpoint), http.WithDiscovery(dis)) log.Infof("new http client with consul: %s", conf.Endpoint) - } else if conf.Etcd.ServiceName != "" { - endpoint := fmt.Sprintf("discovery:///%s", conf.Etcd.ServiceName) + case conf.Etcd.ServiceName != "": + endpoint := "discovery:///" + conf.Etcd.ServiceName cli, err := clientv3.New(clientv3.Config{ Endpoints: []string{conf.Endpoint}, Username: conf.Etcd.Username, @@ -679,7 +702,7 @@ func NewHttpClientConn(ctx context.Context, conf *model.Backend) (*http.Client, dis := etcd.New(cli) opts = append(opts, http.WithEndpoint(endpoint), http.WithDiscovery(dis)) log.Infof("new http client with etcd: %v", conf.Endpoint) - } else { + default: opts = append(opts, http.WithEndpoint(conf.Endpoint)) log.Infof("new http client with endpoint: %s", conf.Endpoint) } diff --git a/internal/version/update.go b/internal/version/update.go index cfec8b4d..ef352536 100644 --- a/internal/version/update.go +++ b/internal/version/update.go @@ -56,7 +56,12 @@ func SelfUpdate(ctx context.Context, url string) error { if err != nil { log.Warnf("self update: rollback: %s -> %s", oldName, currentExecFile) if err := os.Rename(oldName, currentExecFile); err != nil { - log.Errorf("self update: rollback: rename %s -> %s error: %v", oldName, currentExecFile, err) + log.Errorf( + "self update: rollback: rename %s -> %s error: %v", + oldName, + currentExecFile, + err, + ) } } else { log.Debugf("self update: remove old executable file: %s", oldName) diff --git a/internal/version/version.go b/internal/version/version.go index cffc0d7d..dc65a5a8 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -23,11 +23,17 @@ const ( var ( Version = "dev" GitCommit string - _ = settings.NewStringSetting("version", "placeholder string", model.SettingGroupServer, settings.WithBeforeInitString(func(ss settings.StringSetting, s string) (string, error) { - return Version, nil - }), settings.WithBeforeSetString(func(ss settings.StringSetting, s string) (string, error) { - return "", errors.New("version can not be set") - })) + _ = settings.NewStringSetting( + "version", + "placeholder string", + model.SettingGroupServer, + settings.WithBeforeInitString(func(_ settings.StringSetting, _ string) (string, error) { + return Version, nil + }), + settings.WithBeforeSetString(func(_ settings.StringSetting, _ string) (string, error) { + return "", errors.New("version can not be set") + }), + ) ) type Info struct { @@ -155,9 +161,10 @@ func (v *Info) NeedUpdate(ctx context.Context) (bool, error) { } func (v *Info) SelfUpdate(ctx context.Context) (err error) { - if flags.Global.Dev { + switch { + case flags.Global.Dev: log.Info("self update: dev mode, update to latest dev version") - } else if v.Current() != "dev" { + case v.Current() != "dev": latest, err := v.Latest(ctx) if err != nil { return err @@ -171,12 +178,20 @@ func (v *Info) SelfUpdate(ctx context.Context) (err error) { log.Infof("self update: current version is latest: %s", v.Current()) return nil case utils.VersionLess: - log.Infof("self update: current version is less than latest: %s -> %s", v.Current(), latest) + log.Infof( + "self update: current version is less than latest: %s -> %s", + v.Current(), + latest, + ) case utils.VersionGreater: - log.Infof("self update: current version is greater than latest: %s ? %s", v.Current(), latest) + log.Infof( + "self update: current version is greater than latest: %s ? %s", + v.Current(), + latest, + ) return nil } - } else { + default: log.Info("self update: current version is dev, force update") } diff --git a/internal/version/version_test.go b/internal/version/version_test.go index 64d02f24..0a3722e6 100644 --- a/internal/version/version_test.go +++ b/internal/version/version_test.go @@ -1,7 +1,6 @@ package version_test import ( - "context" "testing" "github.com/synctv-org/synctv/internal/version" @@ -12,7 +11,7 @@ func TestCheckLatest(t *testing.T) { if err != nil { t.Fatal(err) } - s, err := v.CheckLatest(context.Background()) + s, err := v.CheckLatest(t.Context()) if err != nil { t.Fatal(err) } @@ -24,7 +23,7 @@ func TestLatestBinaryURL(t *testing.T) { if err != nil { t.Fatal(err) } - s, err := v.LatestBinaryURL(context.Background()) + s, err := v.LatestBinaryURL(t.Context()) if err != nil { t.Fatal(err) } diff --git a/server/handlers/admin.go b/server/handlers/admin.go index 4843ca57..4dd96c54 100644 --- a/server/handlers/admin.go +++ b/server/handlers/admin.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "maps" "net/http" "slices" "strings" @@ -11,23 +12,22 @@ import ( "github.com/gin-gonic/gin" "github.com/maruel/natural" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/email" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/settings" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" - "golang.org/x/exp/maps" "google.golang.org/grpc/connectivity" "gorm.io/gorm" ) func AdminEditSettings(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) req := model.AdminSettingsReq{} if err := model.Decode(ctx, &req); err != nil { @@ -48,8 +48,8 @@ func AdminEditSettings(ctx *gin.Context) { } func AdminSettings(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) group := ctx.Param("group") switch group { @@ -88,7 +88,10 @@ func AdminSettings(ctx *gin.Context) { s, ok := settings.GroupSettings[group] if !ok { log.Error("group not found") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("group not found")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("group not found"), + ) return } data := make(map[string]any, len(s)) @@ -103,8 +106,8 @@ func AdminSettings(ctx *gin.Context) { } func AdminGetUsers(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -175,7 +178,10 @@ func AdminGetUsers(ctx *gin.Context) { } default: log.Error("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -206,8 +212,8 @@ func genUserListResp(us []*dbModel.User) []*model.UserInfoResp { } func AdminGetRoomMembers(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -290,7 +296,10 @@ func AdminGetRoomMembers(ctx *gin.Context) { } default: log.Errorf("get room users failed: not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -330,7 +339,7 @@ func genRoomMemberListResp(us []*dbModel.User, room *op.Room) []*model.RoomMembe } func AdminApprovePendingUser(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -348,7 +357,10 @@ func AdminApprovePendingUser(ctx *gin.Context) { if !user.IsPending() { log.Error("user is not pending") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user is not pending")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user is not pending"), + ) return } @@ -363,8 +375,8 @@ func AdminApprovePendingUser(ctx *gin.Context) { } func AdminBanUser(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -374,7 +386,10 @@ func AdminBanUser(ctx *gin.Context) { if req.ID == user.ID { log.Error("cannot ban self") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot ban self")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot ban self"), + ) return } @@ -387,13 +402,19 @@ func AdminBanUser(ctx *gin.Context) { if u.Value().IsRoot() { log.Error("cannot ban root") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot ban root")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot ban root"), + ) return } if u.Value().IsAdmin() && !user.IsRoot() { log.Error("cannot ban admin") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot ban admin")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot ban admin"), + ) return } @@ -408,8 +429,8 @@ func AdminBanUser(ctx *gin.Context) { } func AdminUnBanUser(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -426,7 +447,10 @@ func AdminUnBanUser(ctx *gin.Context) { if !u.Value().IsBanned() { log.Error("user is not banned") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user is not banned")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user is not banned"), + ) return } @@ -441,8 +465,8 @@ func AdminUnBanUser(ctx *gin.Context) { } func AdminGetRooms(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -513,7 +537,10 @@ func AdminGetRooms(ctx *gin.Context) { } default: log.Error("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -531,7 +558,7 @@ func AdminGetRooms(ctx *gin.Context) { } func AdminGetUserRooms(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) id := ctx.Query("id") if len(id) != 32 { @@ -594,7 +621,10 @@ func AdminGetUserRooms(ctx *gin.Context) { } default: log.Error("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -612,7 +642,7 @@ func AdminGetUserRooms(ctx *gin.Context) { } func AdminGetUserJoinedRooms(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) id := ctx.Query("id") if len(id) != 32 { @@ -631,7 +661,11 @@ func AdminGetUserJoinedRooms(ctx *gin.Context) { scopes := []func(db *gorm.DB) *gorm.DB{ func(db *gorm.DB) *gorm.DB { return db. - InnerJoins("JOIN room_members ON rooms.id = room_members.room_id AND room_members.user_id = ? AND rooms.creator_id != ?", id, id) + InnerJoins( + "JOIN room_members ON rooms.id = room_members.room_id AND room_members.user_id = ? AND rooms.creator_id != ?", + id, + id, + ) }, func(db *gorm.DB) *gorm.DB { return db.Preload("RoomMembers", func(db *gorm.DB) *gorm.DB { @@ -683,7 +717,10 @@ func AdminGetUserJoinedRooms(ctx *gin.Context) { } default: log.Errorf("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -701,7 +738,7 @@ func AdminGetUserJoinedRooms(ctx *gin.Context) { } func AdminApprovePendingRoom(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -720,7 +757,10 @@ func AdminApprovePendingRoom(ctx *gin.Context) { if !room.IsPending() { log.Error("room is not pending") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("room is not pending")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("room is not pending"), + ) return } @@ -735,8 +775,8 @@ func AdminApprovePendingRoom(ctx *gin.Context) { } func AdminBanRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -764,13 +804,19 @@ func AdminBanRoom(ctx *gin.Context) { if creator.IsRoot() { log.Error("cannot ban root") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot ban root")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot ban root"), + ) return } if creator.IsAdmin() && !user.IsRoot() { log.Error("cannot ban admin") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot ban admin")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot ban admin"), + ) return } } @@ -786,8 +832,8 @@ func AdminBanRoom(ctx *gin.Context) { } func AdminUnBanRoom(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -806,7 +852,10 @@ func AdminUnBanRoom(ctx *gin.Context) { if !room.IsBanned() { log.Error("room is not banned") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("room is not banned")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("room is not banned"), + ) return } @@ -821,8 +870,8 @@ func AdminUnBanRoom(ctx *gin.Context) { } func AdminAddUser(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.AddUserReq{} if err := model.Decode(ctx, &req); err != nil { @@ -832,7 +881,10 @@ func AdminAddUser(ctx *gin.Context) { if req.Role == dbModel.RoleRoot && !user.IsRoot() { log.Error("cannot add root user") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("you cannot add root user")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("you cannot add root user"), + ) return } @@ -847,8 +899,8 @@ func AdminAddUser(ctx *gin.Context) { } func AdminDeleteUser(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -865,19 +917,28 @@ func AdminDeleteUser(ctx *gin.Context) { if u.Value().ID == user.ID { log.Error("cannot delete yourself") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot delete yourself")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot delete yourself"), + ) return } if u.Value().IsRoot() { log.Error("cannot delete root") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot delete root")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot delete root"), + ) return } if u.Value().IsAdmin() && !user.IsRoot() { log.Error("cannot delete admin") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot delete admin")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot delete admin"), + ) return } @@ -891,8 +952,8 @@ func AdminDeleteUser(ctx *gin.Context) { } func AdminDeleteRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -920,13 +981,19 @@ func AdminDeleteRoom(ctx *gin.Context) { if creator.IsRoot() { log.Error("cannot delete root's room") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot delete root's room")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot delete root's room"), + ) return } if creator.IsAdmin() && !user.IsRoot() { log.Error("cannot delete admin's room") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot delete admin's room")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot delete admin's room"), + ) return } } @@ -941,8 +1008,8 @@ func AdminDeleteRoom(ctx *gin.Context) { } func AdminUserPassword(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.AdminUserPasswordReq{} if err := model.Decode(ctx, &req); err != nil { @@ -953,27 +1020,39 @@ func AdminUserPassword(ctx *gin.Context) { u, err := op.LoadOrInitUserByID(req.ID) if err != nil { log.Errorf("load or init user by id error: %v", err) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user not found")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user not found"), + ) return } if u.Value().ID != user.ID { if u.Value().IsRoot() { log.Error("cannot change root password") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot change root password")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot change root password"), + ) return } if u.Value().IsAdmin() && !user.IsRoot() { log.Error("cannot change admin password") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot change admin password")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot change admin password"), + ) return } } if err := u.Value().SetPassword(req.Password); err != nil { log.Errorf("set password error: %v", err) - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp(err.Error())) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp(err.Error()), + ) return } @@ -981,8 +1060,8 @@ func AdminUserPassword(ctx *gin.Context) { } func AdminUsername(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.AdminUsernameReq{} if err := model.Decode(ctx, &req); err != nil { @@ -993,27 +1072,39 @@ func AdminUsername(ctx *gin.Context) { u, err := op.LoadOrInitUserByID(req.ID) if err != nil { log.Errorf("load or init user by id error: %v", err) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user not found")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user not found"), + ) return } if u.Value().ID != user.ID { if u.Value().IsRoot() { log.Error("cannot change root username") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot change root username")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot change root username"), + ) return } if u.Value().IsAdmin() && !user.IsRoot() { log.Error("cannot change admin username") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot change admin username")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot change admin username"), + ) return } } if err := u.Value().SetUsername(req.Username); err != nil { log.Errorf("set username error: %v", err) - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp(err.Error())) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp(err.Error()), + ) return } @@ -1021,8 +1112,8 @@ func AdminUsername(ctx *gin.Context) { } func AdminRoomPassword(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.AdminRoomPasswordReq{} if err := model.Decode(ctx, &req); err != nil { @@ -1033,7 +1124,10 @@ func AdminRoomPassword(ctx *gin.Context) { roomE, err := op.LoadOrInitRoomByID(req.ID) if err != nil { log.Errorf("load or init room by id error: %v", err) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("room not found")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("room not found"), + ) return } @@ -1043,26 +1137,38 @@ func AdminRoomPassword(ctx *gin.Context) { creator, err := op.LoadOrInitUserByID(room.CreatorID) if err != nil { log.Errorf("load or init user by id error: %v", err) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("room creator not found")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("room creator not found"), + ) return } if creator.Value().IsRoot() { log.Error("cannot change root room password") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot change root room password")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot change root room password"), + ) return } if creator.Value().IsAdmin() && !user.IsRoot() { log.Error("cannot change admin room password") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("cannot change admin room password")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("cannot change admin room password"), + ) return } } if err := room.SetPassword(req.Password); err != nil { log.Errorf("set password error: %v", err) - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp(err.Error())) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp(err.Error()), + ) return } @@ -1070,8 +1176,8 @@ func AdminRoomPassword(ctx *gin.Context) { } func AdminGetVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) conns := vendor.LoadConns() page, size, err := utils.GetPageAndMax(ctx) @@ -1080,7 +1186,7 @@ func AdminGetVendorBackends(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(err)) return } - s := maps.Keys(conns) + s := slices.Collect(maps.Keys(conns)) l := len(s) var resp []*model.GetVendorBackendResp if (page-1)*size <= l { @@ -1113,8 +1219,8 @@ func AdminGetVendorBackends(ctx *gin.Context) { } func AdminAddVendorBackend(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.AddVendorBackendReq if err := model.Decode(ctx, &req); err != nil { @@ -1132,8 +1238,8 @@ func AdminAddVendorBackend(ctx *gin.Context) { } func AdminDeleteVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { @@ -1151,8 +1257,8 @@ func AdminDeleteVendorBackends(ctx *gin.Context) { } func AdminUpdateVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.AddVendorBackendReq if err := model.Decode(ctx, &req); err != nil { @@ -1170,8 +1276,8 @@ func AdminUpdateVendorBackends(ctx *gin.Context) { } func AdminReconnectVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { @@ -1202,8 +1308,8 @@ func AdminReconnectVendorBackends(ctx *gin.Context) { } func AdminEnableVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { @@ -1221,8 +1327,8 @@ func AdminEnableVendorBackends(ctx *gin.Context) { } func AdminDisableVendorBackends(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { @@ -1240,8 +1346,8 @@ func AdminDisableVendorBackends(ctx *gin.Context) { } func AdminSendTestEmail(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.SendTestEmailReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/danmu.go b/server/handlers/danmu.go index 30f17dde..a4702daa 100644 --- a/server/handlers/danmu.go +++ b/server/handlers/danmu.go @@ -5,17 +5,16 @@ import ( "net/http" "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/server/handlers/vendors" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) func StreamDanmu(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) - room := ctx.MustGet("room").(*op.RoomEntry).Value() - // user := ctx.MustGet("user").(*op.UserEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() + // user := middlewares.GetUserEntry(ctx).Value() m, err := room.GetMovieByID(ctx.Param("movieId")) if err != nil { @@ -34,7 +33,10 @@ func StreamDanmu(ctx *gin.Context) { danmu, ok := v.(vendors.VendorDanmuService) if !ok { log.Errorf("vendor %s not support danmu", m.VendorInfo.Vendor) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("vendor not support danmu")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("vendor not support danmu"), + ) return } diff --git a/server/handlers/init.go b/server/handlers/init.go index cf9c883f..f307e9f6 100644 --- a/server/handlers/init.go +++ b/server/handlers/init.go @@ -60,7 +60,7 @@ func Init(e *gin.Engine) { } } -func initAdmin(admin *gin.RouterGroup, root *gin.RouterGroup) { +func initAdmin(admin, root *gin.RouterGroup) { { admin.GET("/settings", AdminSettings) @@ -135,7 +135,7 @@ func initAdmin(admin *gin.RouterGroup, root *gin.RouterGroup) { } } -func initRoom(room *gin.RouterGroup, needAuthUser *gin.RouterGroup, needAuthRoom *gin.RouterGroup, needAuthWithoutGuestRoom *gin.RouterGroup) { +func initRoom(room, needAuthUser, needAuthRoom, needAuthWithoutGuestRoom *gin.RouterGroup) { room.GET("/check", CheckRoom) room.GET("/hot", RoomHotList) @@ -192,7 +192,7 @@ func initRoom(room *gin.RouterGroup, needAuthUser *gin.RouterGroup, needAuthRoom } } -func initMovie(movie *gin.RouterGroup, needAuthMovie *gin.RouterGroup) { +func initMovie(movie, needAuthMovie *gin.RouterGroup) { // needAuthMovie.GET("/list", MovieList) needAuthMovie.GET("/current", CurrentMovie) @@ -235,7 +235,7 @@ func initMovie(movie *gin.RouterGroup, needAuthMovie *gin.RouterGroup) { needAuthMovie.GET("/danmu/:movieId", StreamDanmu) } -func initUser(user *gin.RouterGroup, needAuthUser *gin.RouterGroup) { +func initUser(user, needAuthUser *gin.RouterGroup) { user.POST("/login", LoginUser) user.POST("/signup", UserSignupPassword) diff --git a/server/handlers/member.go b/server/handlers/member.go index 000530b5..3f425eb6 100644 --- a/server/handlers/member.go +++ b/server/handlers/member.go @@ -4,18 +4,17 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "gorm.io/gorm" ) func RoomMembers(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -89,7 +88,10 @@ func RoomMembers(ctx *gin.Context) { } default: log.Errorf("get room users failed: not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -107,8 +109,8 @@ func RoomMembers(ctx *gin.Context) { } func RoomAdminMembers(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -191,7 +193,10 @@ func RoomAdminMembers(ctx *gin.Context) { } default: log.Errorf("get room users failed: not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -209,9 +214,9 @@ func RoomAdminMembers(ctx *gin.Context) { } func RoomAdminApproveMember(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomApproveMemberReq if err := model.Decode(ctx, &req); err != nil { @@ -231,9 +236,9 @@ func RoomAdminApproveMember(ctx *gin.Context) { } func RoomAdminDeleteMember(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomApproveMemberReq if err := model.Decode(ctx, &req); err != nil { @@ -253,9 +258,9 @@ func RoomAdminDeleteMember(ctx *gin.Context) { } func RoomAdminBanMember(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomBanMemberReq if err := model.Decode(ctx, &req); err != nil { @@ -275,9 +280,9 @@ func RoomAdminBanMember(ctx *gin.Context) { } func RoomAdminUnbanMember(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomUnbanMemberReq if err := model.Decode(ctx, &req); err != nil { @@ -297,9 +302,9 @@ func RoomAdminUnbanMember(ctx *gin.Context) { } func RoomSetMemberPermissions(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomSetMemberPermissionsReq if err := model.Decode(ctx, &req); err != nil { @@ -319,9 +324,9 @@ func RoomSetMemberPermissions(ctx *gin.Context) { } func RoomSetAdmin(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomSetAdminReq if err := model.Decode(ctx, &req); err != nil { @@ -341,9 +346,9 @@ func RoomSetAdmin(ctx *gin.Context) { } func RoomSetMember(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomSetMemberReq if err := model.Decode(ctx, &req); err != nil { @@ -363,9 +368,9 @@ func RoomSetMember(ctx *gin.Context) { } func RoomSetAdminPermissions(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.RoomSetAdminPermissionsReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/movie.go b/server/handlers/movie.go index 40621538..30ffd361 100644 --- a/server/handlers/movie.go +++ b/server/handlers/movie.go @@ -16,7 +16,6 @@ import ( "strings" "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/conf" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -24,6 +23,7 @@ import ( "github.com/synctv-org/synctv/internal/settings" "github.com/synctv-org/synctv/server/handlers/proxy" "github.com/synctv-org/synctv/server/handlers/vendors" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/zijiren233/livelib/protocol/hls" @@ -55,11 +55,12 @@ func genMovieInfo( return nil, errors.New("movie is static folder, can't get movie info") } } - movie := opMovie.Movie.Clone() - if movie.MovieBase.Type == "" && movie.MovieBase.URL != "" { - movie.MovieBase.Type = utils.GetURLExtension(movie.MovieBase.URL) + movie := opMovie.Clone() + if movie.Type == "" && movie.URL != "" { + movie.Type = utils.GetURLExtension(movie.URL) } - if movie.MovieBase.VendorInfo.Vendor != "" { + switch { + case movie.VendorInfo.Vendor != "": vendor, err := vendors.NewVendorService(room, opMovie) if err != nil { return nil, err @@ -68,32 +69,57 @@ func genMovieInfo( if err != nil { return nil, err } - } else if movie.MovieBase.RtmpSource { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/live/hls/list/%s.m3u8?token=%s&roomId=%s", movie.ID, userToken, opMovie.RoomID) - movie.MovieBase.Type = "m3u8" + case movie.RtmpSource: + movie.URL = fmt.Sprintf( + "/api/room/movie/live/hls/list/%s.m3u8?token=%s&roomId=%s", + movie.ID, + userToken, + opMovie.RoomID, + ) + movie.Type = "m3u8" movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: "flv", - URL: fmt.Sprintf("/api/room/movie/live/flv/%s.flv?token=%s&roomId=%s", movie.ID, userToken, opMovie.RoomID), + URL: fmt.Sprintf( + "/api/room/movie/live/flv/%s.flv?token=%s&roomId=%s", + movie.ID, + userToken, + opMovie.RoomID, + ), Type: "flv", }) - movie.MovieBase.Headers = nil - } else if movie.MovieBase.Live && movie.MovieBase.Proxy { - if !utils.IsM3u8Url(movie.MovieBase.URL) { + movie.Headers = nil + case movie.Live && movie.Proxy: + if !utils.IsM3u8Url(movie.URL) { movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: "flv", - URL: fmt.Sprintf("/api/room/movie/live/flv/%s.flv?token=%s&roomId=%s", movie.ID, userToken, opMovie.RoomID), + URL: fmt.Sprintf( + "/api/room/movie/live/flv/%s.flv?token=%s&roomId=%s", + movie.ID, + userToken, + opMovie.RoomID, + ), Type: "flv", }) } - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/live/hls/list/%s.m3u8?token=%s&roomId=%s", movie.ID, userToken, opMovie.RoomID) - movie.MovieBase.Type = "m3u8" - movie.MovieBase.Headers = nil - } else if movie.MovieBase.Proxy { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, opMovie.RoomID) - movie.MovieBase.Headers = nil + movie.URL = fmt.Sprintf( + "/api/room/movie/live/hls/list/%s.m3u8?token=%s&roomId=%s", + movie.ID, + userToken, + opMovie.RoomID, + ) + movie.Type = "m3u8" + movie.Headers = nil + case movie.Proxy: + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + opMovie.RoomID, + ) + movie.Headers = nil } - if movie.MovieBase.Type == "" && movie.MovieBase.URL != "" { - movie.MovieBase.Type = utils.GetURLExtension(movie.MovieBase.URL) + if movie.Type == "" && movie.URL != "" { + movie.Type = utils.GetURLExtension(movie.URL) } for _, v := range movie.MoreSources { if v.Type == "" { @@ -116,7 +142,12 @@ func genMovieInfo( return resp, nil } -func genCurrentRespWithCurrent(ctx context.Context, room *op.Room, user *op.User, userAgent string, userToken string) (*model.CurrentMovieResp, error) { +func genCurrentRespWithCurrent( + ctx context.Context, + room *op.Room, + user *op.User, + userAgent, userToken string, +) (*model.CurrentMovieResp, error) { current := room.Current() if current.Movie.ID == "" { return &model.CurrentMovieResp{ @@ -144,11 +175,17 @@ func genCurrentRespWithCurrent(ctx context.Context, room *op.Room, user *op.User } func CurrentMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) - - currentResp, err := genCurrentRespWithCurrent(ctx, room, user, ctx.GetHeader("User-Agent"), ctx.GetString("token")) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) + + currentResp, err := genCurrentRespWithCurrent( + ctx, + room, + user, + ctx.GetHeader("User-Agent"), + ctx.GetString("token"), + ) if err != nil { log.Errorf("gen current resp error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -159,18 +196,24 @@ func CurrentMovie(ctx *gin.Context) { } func Movies(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) if !user.HasRoomPermission(room, dbModel.PermissionGetMovieList) { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(dbModel.ErrNoPermission)) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorResp(dbModel.ErrNoPermission), + ) return } id := ctx.Query("id") if len(id) != 0 && len(id) != 32 { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id length must be 0 or 32")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id length must be 0 or 32"), + ) return } @@ -189,11 +232,23 @@ func Movies(ctx *gin.Context) { return } if !mv.IsFolder { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("parent id is not folder")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("parent id is not folder"), + ) return } if mv.IsDynamicFolder() { - resp, err := listVendorDynamicMovie(ctx, user, room, mv, ctx.Query("subPath"), ctx.Query("keyword"), page, _max) + resp, err := listVendorDynamicMovie( + ctx, + user, + room, + mv, + ctx.Query("subPath"), + ctx.Query("keyword"), + page, + _max, + ) if err != nil { log.Errorf("vendor dynamic movie list error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -235,7 +290,7 @@ func Movies(ctx *gin.Context) { CreatorID: v.CreatorID, } // hide url and headers when proxy - if user.ID != v.CreatorID && v.MovieBase.Proxy { + if user.ID != v.CreatorID && v.Proxy { resp.Movies[i].Base.URL = "" resp.Movies[i].Base.Headers = nil } @@ -252,7 +307,7 @@ func getParentMoviePath(room *op.Room, id string) ([]*model.MoviePath, error) { return nil, fmt.Errorf("get movie by id error: %w", err) } paths = append(paths, &model.MoviePath{ - Name: p.MovieBase.Name, + Name: p.Name, ID: p.ID, }) id = p.ParentID.String() @@ -265,7 +320,14 @@ func getParentMoviePath(room *op.Room, id string) ([]*model.MoviePath, error) { return paths, nil } -func listVendorDynamicMovie(ctx context.Context, reqUser *op.User, room *op.Room, movie *op.Movie, subPath string, keyword string, page, _max int) (*model.MoviesResp, error) { +func listVendorDynamicMovie( + ctx context.Context, + reqUser *op.User, + room *op.Room, + movie *op.Movie, + subPath, keyword string, + page, _max int, +) (*model.MoviesResp, error) { if reqUser.ID != movie.CreatorID { return nil, fmt.Errorf("list vendor dynamic folder error: %w", dbModel.ErrNoPermission) } @@ -291,9 +353,9 @@ func listVendorDynamicMovie(ctx context.Context, reqUser *op.User, room *op.Room } func PushMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.PushMovieReq{} if err := model.Decode(ctx, &req); err != nil { @@ -322,9 +384,9 @@ func PushMovie(ctx *gin.Context) { } func PushMovies(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.PushMoviesReq{} if err := model.Decode(ctx, &req); err != nil { @@ -358,16 +420,19 @@ func PushMovies(ctx *gin.Context) { } func NewPublishKey(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) if !conf.Conf.Server.RTMP.Enable { log.Errorf("rtmp is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("rtmp is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("rtmp is not enabled"), + ) return } - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() req := model.IDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -382,7 +447,7 @@ func NewPublishKey(ctx *gin.Context) { return } - if movie.Movie.CreatorID != user.ID { + if movie.CreatorID != user.ID { log.Errorf("new publish key error: %v", dbModel.ErrNoPermission) ctx.AbortWithStatusJSON( http.StatusForbidden, @@ -393,13 +458,16 @@ func NewPublishKey(ctx *gin.Context) { return } - if !movie.Movie.MovieBase.RtmpSource { + if !movie.RtmpSource { log.Errorf("new publish key error: %v", "only rtmp source movie can get publish key") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("only live movie can get publish key")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("only live movie can get publish key"), + ) return } - token, err := rtmp.NewRtmpAuthorization(movie.Movie.ID) + token, err := rtmp.NewRtmpAuthorization(movie.ID) if err != nil { log.Errorf("new publish key error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -428,9 +496,9 @@ func NewPublishKey(ctx *gin.Context) { } func EditMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.EditMovieReq{} if err := model.Decode(ctx, &req); err != nil { @@ -458,9 +526,9 @@ func EditMovie(ctx *gin.Context) { } func DelMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.IDsReq{} if err := model.Decode(ctx, &req); err != nil { @@ -489,8 +557,8 @@ func DelMovie(ctx *gin.Context) { } func ClearMovies(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() var req model.ClearMoviesReq if err := model.Decode(ctx, &req); err != nil { @@ -516,8 +584,8 @@ func ClearMovies(ctx *gin.Context) { } func SwapMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() req := model.SwapMovieReq{} if err := model.Decode(ctx, &req); err != nil { @@ -534,9 +602,9 @@ func SwapMovie(ctx *gin.Context) { } func ChangeCurrentMovie(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.SetRoomCurrentMovieReq{} err := model.Decode(ctx, &req) @@ -565,10 +633,10 @@ func ChangeCurrentMovie(ctx *gin.Context) { } func ProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) - room := ctx.MustGet("room").(*op.RoomEntry).Value() - // user := ctx.MustGet("user").(*op.UserEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() + // user := middlewares.GetUserEntry(ctx).Value() m, err := room.GetMovieByID(ctx.Param("movieId")) if err != nil { @@ -577,7 +645,7 @@ func ProxyMovie(ctx *gin.Context) { return } - if m.Movie.MovieBase.VendorInfo.Vendor != "" { + if m.VendorInfo.Vendor != "" { vendor, err := vendors.NewVendorService(room, m) if err != nil { log.Errorf("get vendor service error: %v", err) @@ -590,29 +658,40 @@ func ProxyMovie(ctx *gin.Context) { if !settings.MovieProxy.Get() { log.Errorf("proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } - if !m.Movie.MovieBase.Proxy { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("movie is not proxy")) + if !m.Proxy { + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("movie is not proxy"), + ) return } - if m.Movie.MovieBase.Live || m.Movie.MovieBase.RtmpSource { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("this movie is live or rtmp source, not support use this method proxy")) + if m.Live || m.RtmpSource { + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp( + "this movie is live or rtmp source, not support use this method proxy", + ), + ) return } - switch m.Movie.MovieBase.Type { + switch m.Type { case "mpd": // TODO: cache mpd file fallthrough default: err = proxy.AutoProxyURL(ctx, - m.Movie.MovieBase.URL, - m.Movie.MovieBase.Type, - m.Movie.MovieBase.Headers, + m.URL, + m.Type, + m.Headers, ctx.GetString("token"), room.ID, m.ID, @@ -626,15 +705,18 @@ func ProxyMovie(ctx *gin.Context) { } func ServeM3u8(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) if !settings.MovieProxy.Get() { log.Errorf("movie proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("movie proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("movie proxy is not enabled"), + ) return } - room := ctx.MustGet("room").(*op.RoomEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() m, err := room.GetMovieByID(ctx.Param("movieId")) if err != nil { @@ -643,13 +725,21 @@ func ServeM3u8(ctx *gin.Context) { return } - if m.Movie.MovieBase.RtmpSource { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("this movie is rtmp source, not support use this method proxy")) + if m.RtmpSource { + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp( + "this movie is rtmp source, not support use this method proxy", + ), + ) return } - if !m.Movie.MovieBase.Proxy { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("movie is not proxy")) + if !m.Proxy { + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("movie is not proxy"), + ) return } @@ -666,7 +756,7 @@ func ServeM3u8(ctx *gin.Context) { } err = proxy.M3u8(ctx, claims.TargetURL, - m.Movie.MovieBase.Headers, + m.Headers, claims.IsM3u8File, ctx.GetString("token"), room.ID, @@ -724,10 +814,10 @@ func (e FormatNotSupportFileTypeError) Error() string { } func JoinFlvLive(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) ctx.Header("Cache-Control", "no-store") - room := ctx.MustGet("room").(*op.RoomEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() movieID := strings.TrimSuffix(strings.Trim(ctx.Param("movieId"), "/"), ".flv") m, err := room.GetMovieByID(movieID) if err != nil { @@ -735,15 +825,21 @@ func JoinFlvLive(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorResp(err)) return } - if !m.Movie.MovieBase.Live { + if !m.Live { log.Error("join hls live error: live is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("live is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("live is not enabled"), + ) return } - if m.Movie.MovieBase.RtmpSource { + if m.RtmpSource { if !conf.Conf.Server.RTMP.Enable { log.Error("join hls live error: rtmp is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("rtmp is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("rtmp is not enabled"), + ) return } } else if !settings.LiveProxy.Get() { @@ -774,10 +870,10 @@ func JoinFlvLive(ctx *gin.Context) { } func JoinHlsLive(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) ctx.Header("Cache-Control", "no-store") - room := ctx.MustGet("room").(*op.RoomEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() movieID := strings.TrimSuffix(strings.Trim(ctx.Param("movieId"), "/"), ".m3u8") m, err := room.GetMovieByID(movieID) if err != nil { @@ -785,15 +881,21 @@ func JoinHlsLive(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorResp(err)) return } - if !m.Movie.MovieBase.Live { + if !m.Live { log.Error("join hls live error: live is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("live is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("live is not enabled"), + ) return } - if m.Movie.MovieBase.RtmpSource { + if m.RtmpSource { if !conf.Conf.Server.RTMP.Enable { log.Error("join hls live error: rtmp is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("rtmp is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("rtmp is not enabled"), + ) return } } else if !settings.LiveProxy.Get() { @@ -802,10 +904,10 @@ func JoinHlsLive(ctx *gin.Context) { return } - if utils.IsM3u8Url(m.Movie.MovieBase.URL) { + if utils.IsM3u8Url(m.URL) { err = proxy.M3u8(ctx, - m.Movie.MovieBase.URL, - m.Movie.MovieBase.Headers, + m.URL, + m.Headers, true, ctx.GetString("token"), room.ID, @@ -829,7 +931,13 @@ func JoinHlsLive(ctx *gin.Context) { if settings.TSDisguisedAsPng.Get() { ext = "png" } - return fmt.Sprintf("/api/room/movie/live/hls/data/%s/%s/%s.%s", room.ID, movieID, tsName, ext) + return fmt.Sprintf( + "/api/room/movie/live/hls/data/%s/%s/%s.%s", + room.ID, + movieID, + tsName, + ext, + ) }) if err != nil { log.Errorf("join hls live error: %v", err) @@ -839,8 +947,9 @@ func JoinHlsLive(ctx *gin.Context) { ctx.Data(http.StatusOK, hls.M3U8ContentType, b) } +//nolint:gosec func ServeHlsLive(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) roomID := ctx.Param("roomId") roomE, err := op.LoadRoomByID(roomID) if err != nil { @@ -859,15 +968,21 @@ func ServeHlsLive(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorResp(err)) return } - if !m.Movie.MovieBase.Live { + if !m.Live { log.Error("join hls live error: live is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("live is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("live is not enabled"), + ) return } - if m.Movie.MovieBase.RtmpSource { + if m.RtmpSource { if !conf.Conf.Server.RTMP.Enable { log.Error("join hls live error: rtmp is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("rtmp is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("rtmp is not enabled"), + ) return } } else if !settings.LiveProxy.Get() { @@ -887,7 +1002,10 @@ func ServeHlsLive(ctx *gin.Context) { case ".ts": if settings.TSDisguisedAsPng.Get() { log.Errorf("serve hls live error: %v", FormatNotSupportFileTypeError(fileExt)) - ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt))) + ctx.AbortWithStatusJSON( + http.StatusNotFound, + model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt)), + ) return } b, err := channel.GetTsFile(strings.TrimSuffix(dataID, fileExt)) @@ -901,7 +1019,10 @@ func ServeHlsLive(ctx *gin.Context) { case ".png": if !settings.TSDisguisedAsPng.Get() { log.Errorf("serve hls live error: %v", FormatNotSupportFileTypeError(fileExt)) - ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt))) + ctx.AbortWithStatusJSON( + http.StatusNotFound, + model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt)), + ) return } b, err := channel.GetTsFile(strings.TrimSuffix(dataID, fileExt)) @@ -924,6 +1045,9 @@ func ServeHlsLive(ctx *gin.Context) { default: ctx.Header("Cache-Control", "no-store") log.Errorf("serve hls live error: %v", FormatNotSupportFileTypeError(fileExt)) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorResp(FormatNotSupportFileTypeError(fileExt)), + ) } } diff --git a/server/handlers/proxy/buffer.go b/server/handlers/proxy/buffer.go index ee716f4c..1b6dd882 100644 --- a/server/handlers/proxy/buffer.go +++ b/server/handlers/proxy/buffer.go @@ -11,14 +11,18 @@ const ( ) var sharedBufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { buffer := make([]byte, DefaultBufferSize) return &buffer }, } func getBuffer() *[]byte { - return sharedBufferPool.Get().(*[]byte) + buf, ok := sharedBufferPool.Get().(*[]byte) + if !ok { + panic("sharedBufferPool.Get() returned a non-[]byte value") + } + return buf } func putBuffer(buffer *[]byte) { diff --git a/server/handlers/proxy/m3u8.go b/server/handlers/proxy/m3u8.go index cf84b624..3f6942c9 100644 --- a/server/handlers/proxy/m3u8.go +++ b/server/handlers/proxy/m3u8.go @@ -28,7 +28,7 @@ type M3u8TargetClaims struct { } func GetM3u8Target(token string) (*M3u8TargetClaims, error) { - t, err := jwt.ParseWithClaims(token, &M3u8TargetClaims{}, func(token *jwt.Token) (any, error) { + t, err := jwt.ParseWithClaims(token, &M3u8TargetClaims{}, func(_ *jwt.Token) (any, error) { return stream.StringToBytes(conf.Conf.Jwt.Secret), nil }) if err != nil || !t.Valid { @@ -51,20 +51,25 @@ func NewM3u8TargetToken(targetURL, roomID, movieID string, isM3u8File bool) (str NotBefore: jwt.NewNumericDate(time.Now()), }, } - return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) + return jwt.NewWithClaims(jwt.SigningMethodHS256, claims). + SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) } const maxM3u8FileSize = 3 * 1024 * 1024 // -func M3u8Data(ctx *gin.Context, data []byte, baseURL string, token, roomID, movieID string) error { +func M3u8Data(ctx *gin.Context, data []byte, baseURL, token, roomID, movieID string) error { hasM3u8File := false - err := m3u8.RangeM3u8SegmentsWithBaseURL(stream.BytesToString(data), baseURL, func(segmentUrl string) (bool, error) { - if utils.IsM3u8Url(segmentUrl) { - hasM3u8File = true - return false, nil - } - return true, nil - }) + err := m3u8.RangeM3u8SegmentsWithBaseURL( + stream.BytesToString(data), + baseURL, + func(segmentUrl string) (bool, error) { + if utils.IsM3u8Url(segmentUrl) { + hasM3u8File = true + return false, nil + } + return true, nil + }, + ) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp( @@ -73,13 +78,23 @@ func M3u8Data(ctx *gin.Context, data []byte, baseURL string, token, roomID, movi ) return fmt.Errorf("range m3u8 segments with base url error: %w", err) } - m3u8Str, err := m3u8.ReplaceM3u8SegmentsWithBaseURL(stream.BytesToString(data), baseURL, func(segmentUrl string) (string, error) { - targetToken, err := NewM3u8TargetToken(segmentUrl, roomID, movieID, hasM3u8File) - if err != nil { - return "", err - } - return fmt.Sprintf("/api/room/movie/proxy/%s/m3u8/%s?token=%s&roomId=%s", movieID, targetToken, token, roomID), nil - }) + m3u8Str, err := m3u8.ReplaceM3u8SegmentsWithBaseURL( + stream.BytesToString(data), + baseURL, + func(segmentUrl string) (string, error) { + targetToken, err := NewM3u8TargetToken(segmentUrl, roomID, movieID, hasM3u8File) + if err != nil { + return "", err + } + return fmt.Sprintf( + "/api/room/movie/proxy/%s/m3u8/%s?token=%s&roomId=%s", + movieID, + targetToken, + token, + roomID, + ), nil + }, + ) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp( @@ -93,7 +108,14 @@ func M3u8Data(ctx *gin.Context, data []byte, baseURL string, token, roomID, movi } // only cache non-m3u8 files -func M3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File bool, token, roomID, movieID string, opts ...Option) error { +func M3u8( + ctx *gin.Context, + u string, + headers map[string]string, + isM3u8File bool, + token, roomID, movieID string, + opts ...Option, +) error { if !isM3u8File { return URL(ctx, u, headers, opts...) } @@ -126,16 +148,25 @@ func M3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File bool return fmt.Errorf("do request error: %w", err) } defer resp.Body.Close() - // if contentType := resp.Header.Get("Content-Type"); !strings.HasPrefix(contentType, "application/vnd.apple.mpegurl") { + // if contentType := resp.Header.Get("Content-Type"); !strings.HasPrefix(contentType, + // "application/vnd.apple.mpegurl") { // return fmt.Errorf("m3u8 file is not a valid m3u8 file, content type: %s", contentType) // } if resp.ContentLength > maxM3u8FileSize { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp( - fmt.Sprintf("m3u8 file is too large: %d, max: %d (3MB)", resp.ContentLength, maxM3u8FileSize), + fmt.Sprintf( + "m3u8 file is too large: %d, max: %d (3MB)", + resp.ContentLength, + maxM3u8FileSize, + ), ), ) - return fmt.Errorf("m3u8 file is too large: %d, max: %d (3MB)", resp.ContentLength, maxM3u8FileSize) + return fmt.Errorf( + "m3u8 file is too large: %d, max: %d (3MB)", + resp.ContentLength, + maxM3u8FileSize, + ) } b, err := io.ReadAll(io.LimitReader(resp.Body, maxM3u8FileSize)) if err != nil { diff --git a/server/handlers/proxy/proxy.go b/server/handlers/proxy/proxy.go index 178a38e2..08c3f316 100644 --- a/server/handlers/proxy/proxy.go +++ b/server/handlers/proxy/proxy.go @@ -40,13 +40,14 @@ func parseProxyCacheSize(sizeStr string) (int64, error) { var multiplier int64 = 1024 * 1024 // Default MB - if strings.HasSuffix(sizeStr, "gb") { + switch { + case strings.HasSuffix(sizeStr, "gb"): multiplier = 1024 * 1024 * 1024 sizeStr = strings.TrimSuffix(sizeStr, "gb") - } else if strings.HasSuffix(sizeStr, "mb") { + case strings.HasSuffix(sizeStr, "mb"): multiplier = 1024 * 1024 sizeStr = strings.TrimSuffix(sizeStr, "mb") - } else if strings.HasSuffix(sizeStr, "kb") { + case strings.HasSuffix(sizeStr, "kb"): multiplier = 1024 sizeStr = strings.TrimSuffix(sizeStr, "kb") } @@ -178,7 +179,7 @@ func URL(ctx *gin.Context, u string, headers map[string]string, opts ...Option) } cli := http.Client{ Transport: uhc.DefaultTransport, - CheckRedirect: func(req *http.Request, via []*http.Request) error { + CheckRedirect: func(req *http.Request, _ []*http.Request) error { for k, v := range headers { req.Header.Set(k, v) } @@ -216,7 +217,13 @@ func URL(ctx *gin.Context, u string, headers map[string]string, opts ...Option) return nil } -func AutoProxyURL(ctx *gin.Context, u, t string, headers map[string]string, token, roomID, movieID string, opts ...Option) error { +func AutoProxyURL( + ctx *gin.Context, + u, t string, + headers map[string]string, + token, roomID, movieID string, + opts ...Option, +) error { if strings.HasPrefix(t, "m3u") || utils.IsM3u8Url(u) { return M3u8(ctx, u, headers, true, token, roomID, movieID, opts...) } diff --git a/server/handlers/proxy/readseeker.go b/server/handlers/proxy/readseeker.go index 588199ac..1a284ea9 100644 --- a/server/handlers/proxy/readseeker.go +++ b/server/handlers/proxy/readseeker.go @@ -19,6 +19,7 @@ var ( _ Proxy = (*HTTPReadSeekCloser)(nil) ) +//nolint:containedctx type HTTPReadSeekCloser struct { ctx context.Context headHeaders http.Header @@ -119,7 +120,9 @@ func WithForceNotSupportRange(notSupportRange bool) HTTPReadSeekerConf { } } -func WithNotSupportSeekWhenNotSupportRange(notSupportSeekWhenNotSupportRange bool) HTTPReadSeekerConf { +func WithNotSupportSeekWhenNotSupportRange( + notSupportSeekWhenNotSupportRange bool, +) HTTPReadSeekerConf { return func(h *HTTPReadSeekCloser) { h.notSupportSeekWhenNotSupportRange = notSupportSeekWhenNotSupportRange } @@ -165,7 +168,7 @@ func (h *HTTPReadSeekCloser) fix() *HTTPReadSeekCloser { if h.client == nil { h.client = &http.Client{ Transport: uhc.DefaultTransport, - CheckRedirect: func(req *http.Request, via []*http.Request) error { + CheckRedirect: func(req *http.Request, _ []*http.Request) error { for k, v := range h.headers { req.Header[k] = v } @@ -244,7 +247,11 @@ func (h *HTTPReadSeekCloser) FetchNextChunk() error { h.contentTotalLength = contentTotalLength } resp.Body.Close() - return fmt.Errorf("requested range not satisfiable, content total length: %d, offset: %d", h.contentTotalLength, h.offset) + return fmt.Errorf( + "requested range not satisfiable, content total length: %d, offset: %d", + h.contentTotalLength, + h.offset, + ) } if err := h.checkContentType(resp.Header.Get("Content-Type")); err != nil { @@ -339,7 +346,11 @@ func (h *HTTPReadSeekCloser) closeCurrentResp() { func (h *HTTPReadSeekCloser) checkContentType(ct string) error { if len(h.allowedContentTypes) != 0 { if ct == "" || slices.Index(h.allowedContentTypes, ct) == -1 { - return fmt.Errorf("content type '%s' is not in the list of allowed content types: %v", ct, h.allowedContentTypes) + return fmt.Errorf( + "content type '%s' is not in the list of allowed content types: %v", + ct, + h.allowedContentTypes, + ) } } return nil @@ -444,7 +455,9 @@ func (h *HTTPReadSeekCloser) ContentTotalLength() (int64, error) { if h.contentTotalLength > 0 { return h.contentTotalLength, nil } - return 0, errors.New("content total length is not available - no successful response received yet") + return 0, errors.New( + "content total length is not available - no successful response received yet", + ) } func ParseContentRangeStartAndEnd(contentRange string) (int64, int64, error) { @@ -453,17 +466,26 @@ func ParseContentRangeStartAndEnd(contentRange string) (int64, int64, error) { } if !strings.HasPrefix(contentRange, "bytes ") { - return 0, 0, fmt.Errorf("invalid Content-Range header format (expected 'bytes ' prefix): %s", contentRange) + return 0, 0, fmt.Errorf( + "invalid Content-Range header format (expected 'bytes ' prefix): %s", + contentRange, + ) } parts := strings.Split(strings.TrimPrefix(contentRange, "bytes "), "/") if len(parts) != 2 { - return 0, 0, fmt.Errorf("invalid Content-Range header format (expected 2 parts separated by '/'): %s", contentRange) + return 0, 0, fmt.Errorf( + "invalid Content-Range header format (expected 2 parts separated by '/'): %s", + contentRange, + ) } rangeParts := strings.Split(strings.TrimSpace(parts[0]), "-") if len(rangeParts) != 2 { - return 0, 0, fmt.Errorf("invalid Content-Range range format (expected start-end): %s", contentRange) + return 0, 0, fmt.Errorf( + "invalid Content-Range range format (expected start-end): %s", + contentRange, + ) } start, err := strconv.ParseInt(strings.TrimSpace(rangeParts[0]), 10, 64) @@ -492,12 +514,18 @@ func ParseContentRangeTotalLength(contentRange string) (int64, error) { } if !strings.HasPrefix(contentRange, "bytes ") { - return 0, fmt.Errorf("invalid Content-Range header format (expected 'bytes ' prefix): %s", contentRange) + return 0, fmt.Errorf( + "invalid Content-Range header format (expected 'bytes ' prefix): %s", + contentRange, + ) } parts := strings.Split(strings.TrimPrefix(contentRange, "bytes "), "/") if len(parts) != 2 { - return 0, fmt.Errorf("invalid Content-Range header format (expected 2 parts separated by '/'): %s", contentRange) + return 0, fmt.Errorf( + "invalid Content-Range header format (expected 2 parts separated by '/'): %s", + contentRange, + ) } if parts[1] == "" || parts[1] == "*" { diff --git a/server/handlers/proxy/slice.go b/server/handlers/proxy/slice.go index 4384e730..1b5d8599 100644 --- a/server/handlers/proxy/slice.go +++ b/server/handlers/proxy/slice.go @@ -46,16 +46,11 @@ func NewSliceCacheProxy(key string, sliceSize int64, r Proxy, cache Cache) *Slic } } -func cacheKey(key string, offset int64, sliceSize int64) string { +func cacheKey(key string, offset, sliceSize int64) string { hash := sha256.Sum256(stream.StringToBytes(key)) return fmt.Sprintf("%s-%d-%d", hex.EncodeToString(hash[:]), sliceSize, offset) } -func cachePrefix(key string, sliceSize int64) string { - hash := sha256.Sum256(stream.StringToBytes(key)) - return fmt.Sprintf("%s-%d", hex.EncodeToString(hash[:]), sliceSize) -} - func alignedOffset(offset, sliceSize int64) int64 { return (offset / sliceSize) * sliceSize } @@ -113,7 +108,11 @@ func (c *SliceCacheProxy) Proxy(w http.ResponseWriter, r *http.Request) error { alignedOffset := alignedOffset(byteRange.Start, c.sliceSize) cacheItem, cached, err := c.getCacheItem(alignedOffset) if err != nil { - http.Error(w, fmt.Sprintf("Failed to get cache item: %v", err), http.StatusInternalServerError) + http.Error( + w, + fmt.Sprintf("Failed to get cache item: %v", err), + http.StatusInternalServerError, + ) return fmt.Errorf("failed to get cache item: %w", err) } @@ -126,7 +125,12 @@ func (c *SliceCacheProxy) Proxy(w http.ResponseWriter, r *http.Request) error { const cacheStatusHeader = "X-Cache-Status" -func (c *SliceCacheProxy) setResponseHeaders(w http.ResponseWriter, byteRange *ByteRange, cacheItem *CacheItem, cached bool, isRangeRequest bool) { +func (c *SliceCacheProxy) setResponseHeaders( + w http.ResponseWriter, + byteRange *ByteRange, + cacheItem *CacheItem, + cached, isRangeRequest bool, +) { // Copy headers excluding special ones for k, v := range cacheItem.Metadata.Headers { switch k { @@ -143,23 +147,34 @@ func (c *SliceCacheProxy) setResponseHeaders(w http.ResponseWriter, byteRange *B w.Header().Set(cacheStatusHeader, "MISS") } w.Header().Set("Accept-Ranges", "bytes") - w.Header().Set("Content-Length", fmtContentLength(byteRange.Start, byteRange.End, cacheItem.Metadata.ContentTotalLength)) + w.Header(). + Set("Content-Length", fmtContentLength(byteRange.Start, byteRange.End, cacheItem.Metadata.ContentTotalLength)) w.Header().Set("Content-Type", cacheItem.Metadata.ContentType) if isRangeRequest { - w.Header().Set("Content-Range", fmtContentRange(byteRange.Start, byteRange.End, cacheItem.Metadata.ContentTotalLength)) + w.Header(). + Set("Content-Range", fmtContentRange(byteRange.Start, byteRange.End, cacheItem.Metadata.ContentTotalLength)) w.WriteHeader(http.StatusPartialContent) } else { w.WriteHeader(http.StatusOK) } } -func (c *SliceCacheProxy) writeResponse(w http.ResponseWriter, byteRange *ByteRange, alignedOffset int64, cacheItem *CacheItem) error { +func (c *SliceCacheProxy) writeResponse( + w http.ResponseWriter, + byteRange *ByteRange, + alignedOffset int64, + cacheItem *CacheItem, +) error { sliceOffset := byteRange.Start - alignedOffset if sliceOffset < 0 { return fmt.Errorf("slice offset cannot be negative, got: %d", sliceOffset) } - remainingLength := contentLength(byteRange.Start, byteRange.End, cacheItem.Metadata.ContentTotalLength) + remainingLength := contentLength( + byteRange.Start, + byteRange.End, + cacheItem.Metadata.ContentTotalLength, + ) if remainingLength == 0 { return nil } @@ -204,7 +219,10 @@ func (c *SliceCacheProxy) writeResponse(w http.ResponseWriter, byteRange *ByteRa func (c *SliceCacheProxy) getCacheItem(alignedOffset int64) (*CacheItem, bool, error) { if alignedOffset < 0 { - return nil, false, fmt.Errorf("cache item offset cannot be negative, got: %d", alignedOffset) + return nil, false, fmt.Errorf( + "cache item offset cannot be negative, got: %d", + alignedOffset, + ) } cacheKey := cacheKey(c.key, alignedOffset, c.sliceSize) @@ -259,14 +277,24 @@ func (c *SliceCacheProxy) fetchFromSource(offset int64) (*CacheItem, error) { n, err := io.ReadFull(c.r, buf) if err != nil { if !errors.Is(err, io.ErrUnexpectedEOF) { - return nil, fmt.Errorf("failed to read %d bytes from source at offset %d: %w", c.sliceSize, offset, err) + return nil, fmt.Errorf( + "failed to read %d bytes from source at offset %d: %w", + c.sliceSize, + offset, + err, + ) } total, err = c.contentTotalLength() if err != nil { return nil, fmt.Errorf("failed to get content total length from source: %w", err) } if total != offset+int64(n) { - return nil, fmt.Errorf("source content total length mismatch, got: %d, expected: %d, %w", total, offset+int64(n), io.ErrUnexpectedEOF) + return nil, fmt.Errorf( + "source content total length mismatch, got: %d, expected: %d, %w", + total, + offset+int64(n), + io.ErrUnexpectedEOF, + ) } } @@ -324,7 +352,10 @@ func ParseByteRange(r string) (*ByteRange, error) { r = strings.TrimPrefix(r, "bytes=") parts := strings.Split(r, "-") if len(parts) != 2 { - return nil, fmt.Errorf("range header must contain exactly one hyphen (-) separator, got: %s", r) + return nil, fmt.Errorf( + "range header must contain exactly one hyphen (-) separator, got: %s", + r, + ) } parts[0] = strings.TrimSpace(parts[0]) @@ -356,7 +387,11 @@ func ParseByteRange(r string) (*ByteRange, error) { return nil, fmt.Errorf("range end value must be non-negative, got: %d", end) } if start > end { - return nil, fmt.Errorf("range start value (%d) cannot be greater than end value (%d)", start, end) + return nil, fmt.Errorf( + "range start value (%d) cannot be greater than end value (%d)", + start, + end, + ) } } diff --git a/server/handlers/public.go b/server/handlers/public.go index c5af793f..be30f0b4 100644 --- a/server/handlers/public.go +++ b/server/handlers/public.go @@ -5,10 +5,10 @@ import ( "strings" "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/email" "github.com/synctv-org/synctv/internal/settings" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) @@ -24,7 +24,7 @@ type publicSettings struct { } func Settings(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) oauth2SignupEnabled, err := bootstrap.Oauth2SignupEnabledCache.Get(ctx) if err != nil { @@ -34,10 +34,12 @@ func Settings(ctx *gin.Context) { } ctx.JSON(200, model.NewAPIDataResp( &publicSettings{ - PasswordDisableSignup: settings.DisableUserSignup.Get() || !settings.EnablePasswordSignup.Get(), + PasswordDisableSignup: settings.DisableUserSignup.Get() || + !settings.EnablePasswordSignup.Get(), - EmailEnable: email.EnableEmail.Get(), - EmailDisableSignup: settings.DisableUserSignup.Get() || email.DisableUserSignup.Get(), + EmailEnable: email.EnableEmail.Get(), + EmailDisableSignup: settings.DisableUserSignup.Get() || + email.DisableUserSignup.Get(), EmailWhitelistEnabled: email.EmailSignupWhiteListEnable.Get(), EmailWhitelist: strings.Split(email.EmailSignupWhiteList.Get(), ","), diff --git a/server/handlers/room.go b/server/handlers/room.go index 0a633ee5..4847efd5 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -10,7 +10,6 @@ import ( "github.com/gin-gonic/gin" "github.com/maruel/natural" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -30,9 +29,9 @@ var ( ) func RoomMe(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) member, err := room.LoadMember(user.ID) if err != nil { @@ -53,9 +52,9 @@ func RoomMe(ctx *gin.Context) { } func RoomInfo(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) member, err := room.LoadMember(user.ID) if err != nil { @@ -87,17 +86,20 @@ func RoomInfo(ctx *gin.Context) { } func RoomPiblicSettings(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() + room := middlewares.GetRoomEntry(ctx).Value() ctx.JSON(http.StatusOK, model.NewAPIDataResp(room.Settings)) } func CreateRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) if settings.DisableCreateRoom.Get() && !user.IsAdmin() { log.Error("create room is disabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("create room is disabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("create room is disabled"), + ) return } @@ -108,7 +110,11 @@ func CreateRoom(ctx *gin.Context) { return } - room, err := user.CreateRoom(req.RoomName, req.Password, db.WithSettingHidden(req.Settings.Hidden)) + room, err := user.CreateRoom( + req.RoomName, + req.Password, + db.WithSettingHidden(req.Settings.Hidden), + ) if err != nil { log.Errorf("create room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(err)) @@ -121,44 +127,47 @@ func CreateRoom(ctx *gin.Context) { })) } -var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(context.Context) ([]*model.RoomListResp, error) { - rooms := make([]*model.RoomListResp, 0) - op.RangeRoomCache(func(key string, value *synccache.Entry[*op.Room]) bool { - v := value.Value() - if !v.Settings.Hidden && v.IsActive() && !v.HubIsNotInited() { - rooms = append(rooms, &model.RoomListResp{ - RoomID: v.ID, - RoomName: v.Name, - ViewerCount: v.ViewerCount(), - NeedPassword: v.NeedPassword(), - Creator: op.GetUserName(v.CreatorID), - CreatorID: v.CreatorID, - CreatedAt: v.CreatedAt.UnixMilli(), - }) - } - return true - }) - - slices.SortStableFunc(rooms, func(a, b *model.RoomListResp) int { - if a.ViewerCount == b.ViewerCount { - if a.RoomName == b.RoomName { - return 0 +var roomHotCache = refreshcache0.NewRefreshCache( + func(context.Context) ([]*model.RoomListResp, error) { + rooms := make([]*model.RoomListResp, 0) + op.RangeRoomCache(func(_ string, value *synccache.Entry[*op.Room]) bool { + v := value.Value() + if !v.Settings.Hidden && v.IsActive() && !v.HubIsNotInited() { + rooms = append(rooms, &model.RoomListResp{ + RoomID: v.ID, + RoomName: v.Name, + ViewerCount: v.ViewerCount(), + NeedPassword: v.NeedPassword(), + Creator: op.GetUserName(v.CreatorID), + CreatorID: v.CreatorID, + CreatedAt: v.CreatedAt.UnixMilli(), + }) } - if natural.Less(a.RoomName, b.RoomName) { + return true + }) + + slices.SortStableFunc(rooms, func(a, b *model.RoomListResp) int { + if a.ViewerCount == b.ViewerCount { + if a.RoomName == b.RoomName { + return 0 + } + if natural.Less(a.RoomName, b.RoomName) { + return -1 + } + return 1 + } else if a.ViewerCount > b.ViewerCount { return -1 } return 1 - } else if a.ViewerCount > b.ViewerCount { - return -1 - } - return 1 - }) + }) - return rooms, nil -}, time.Second*3) + return rooms, nil + }, + time.Second*3, +) func RoomHotList(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -181,7 +190,7 @@ func RoomHotList(ctx *gin.Context) { } func RoomList(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -208,7 +217,10 @@ func RoomList(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - scopes = append(scopes, db.WhereRoomNameLikeOrCreatorInOrRoomsIDLike(keyword, ids, keyword)) + scopes = append( + scopes, + db.WhereRoomNameLikeOrCreatorInOrRoomsIDLike(keyword, ids, keyword), + ) case "name": scopes = append(scopes, db.WhereRoomNameLike(keyword)) case "creator": @@ -247,7 +259,10 @@ func RoomList(ctx *gin.Context) { } default: log.Errorf("get room list failed: not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -314,7 +329,7 @@ func genJoinedRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) ([]*model.Joine } func CheckRoom(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) roomID, err := middlewares.GetRoomIDFromContext(ctx) if err != nil { log.Errorf("check room failed: %v", err) @@ -342,8 +357,8 @@ func CheckRoom(ctx *gin.Context) { } func LoginRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.LoginRoomReq{} if err := model.Decode(ctx, &req); err != nil { @@ -368,7 +383,10 @@ func LoginRoom(ctx *gin.Context) { if room.IsPending() { log.Warn("login room failed: room is pending, please wait for admin to approve") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("room is pending, please wait for admin to approve")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("room is pending, please wait for admin to approve"), + ) return } @@ -414,8 +432,8 @@ func LoginRoom(ctx *gin.Context) { } func CheckRoomPassword(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.CheckRoomPasswordReq{} if err := model.Decode(ctx, &req); err != nil { @@ -430,9 +448,9 @@ func CheckRoomPassword(ctx *gin.Context) { } func DeleteRoom(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry) - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) if err := user.DeleteRoom(room); err != nil { log.Errorf("delete room failed: %v", err) @@ -453,9 +471,9 @@ func DeleteRoom(ctx *gin.Context) { } func SetRoomPassword(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.SetRoomPasswordReq{} if err := model.Decode(ctx, &req); err != nil { @@ -483,16 +501,16 @@ func SetRoomPassword(ctx *gin.Context) { } func RoomSetting(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - // user := ctx.MustGet("user").(*op.UserEntry) + room := middlewares.GetRoomEntry(ctx).Value() + // user := middlewares.GetUserEntry(ctx) ctx.JSON(http.StatusOK, model.NewAPIDataResp(room.Settings)) } func SetRoomSetting(ctx *gin.Context) { - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.SetRoomSettingReq{} if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/root.go b/server/handlers/root.go index 25d81703..782e8650 100644 --- a/server/handlers/root.go +++ b/server/handlers/root.go @@ -4,14 +4,14 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/op" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) func RootAddAdmin(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.IDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -22,18 +22,27 @@ func RootAddAdmin(ctx *gin.Context) { if req.ID == user.ID { log.Errorf("cannot add yourself") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot add yourself")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot add yourself"), + ) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { log.Errorf("failed to load user: %v", err) - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("user not found")) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("user not found"), + ) return } if u.Value().IsAdmin() { log.Errorf("user is already admin") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user is already admin")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user is already admin"), + ) return } @@ -47,8 +56,8 @@ func RootAddAdmin(ctx *gin.Context) { } func RootDeleteAdmin(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) req := model.IDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -59,18 +68,27 @@ func RootDeleteAdmin(ctx *gin.Context) { if req.ID == user.Value().ID { log.Errorf("cannot remove yourself") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot remove yourself")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot remove yourself"), + ) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { log.Errorf("failed to load user: %v", err) - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("user not found")) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("user not found"), + ) return } if u.Value().IsRoot() { log.Errorf("cannot remove root") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("cannot remove root")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("cannot remove root"), + ) return } diff --git a/server/handlers/user.go b/server/handlers/user.go index c576f9cd..1ed2d2ca 100644 --- a/server/handlers/user.go +++ b/server/handlers/user.go @@ -5,11 +5,11 @@ import ( "math/rand/v2" "net/http" "net/url" + "slices" "strings" "time" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/captcha" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/email" @@ -22,12 +22,11 @@ import ( "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/zijiren233/gencontainer/synccache" - "golang.org/x/exp/slices" "gorm.io/gorm" ) func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() ctx.JSON(http.StatusOK, model.NewAPIDataResp(&model.UserInfoResp{ ID: user.ID, @@ -39,7 +38,7 @@ func Me(ctx *gin.Context) { } func LoginUser(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.LoginUserReq{} if err := model.Decode(ctx, &req); err != nil { @@ -50,12 +49,16 @@ func LoginUser(ctx *gin.Context) { var user *synccache.Entry[*op.User] var err error - if req.Username != "" { + switch { + case req.Username != "": user, err = op.LoadOrInitUserByUsername(req.Username) - } else if req.Email != "" { + case req.Email != "": user, err = op.LoadOrInitUserByEmail(req.Email) - } else { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("username or email is required")) + default: + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("username or email is required"), + ) return } @@ -75,7 +78,10 @@ func LoginUser(ctx *gin.Context) { if ok := user.Value().CheckPassword(req.Password); !ok { log.Errorf("password incorrect") - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorStringResp("password incorrect")) + ctx.AbortWithStatusJSON( + http.StatusForbidden, + model.NewAPIErrorStringResp("password incorrect"), + ) return } @@ -83,7 +89,7 @@ func LoginUser(ctx *gin.Context) { } func handleUserToken(ctx *gin.Context, user *op.User) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) token, err := middlewares.NewAuthUserToken(user) if err != nil { @@ -107,8 +113,8 @@ func handleUserToken(ctx *gin.Context, user *op.User) { } func LogoutUser(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry) - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx) + log := middlewares.GetLogger(ctx) err := op.CompareAndDeleteUser(user) if err != nil { @@ -121,8 +127,8 @@ func LogoutUser(ctx *gin.Context) { } func UserRooms(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -185,7 +191,10 @@ func UserRooms(ctx *gin.Context) { } default: log.Errorf("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -203,8 +212,8 @@ func UserRooms(ctx *gin.Context) { } func UserJoinedRooms(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { @@ -216,7 +225,11 @@ func UserJoinedRooms(ctx *gin.Context) { scopes := []func(db *gorm.DB) *gorm.DB{ func(db *gorm.DB) *gorm.DB { return db. - InnerJoins("JOIN room_members ON rooms.id = room_members.room_id AND room_members.user_id = ? AND rooms.creator_id != ?", user.ID, user.ID) + InnerJoins( + "JOIN room_members ON rooms.id = room_members.room_id AND room_members.user_id = ? AND rooms.creator_id != ?", + user.ID, + user.ID, + ) }, func(db *gorm.DB) *gorm.DB { return db.Preload("RoomMembers", func(db *gorm.DB) *gorm.DB { @@ -259,7 +272,10 @@ func UserJoinedRooms(ctx *gin.Context) { } default: log.Errorf("not support sort") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("not support sort")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("not support sort"), + ) return } @@ -277,8 +293,8 @@ func UserJoinedRooms(ctx *gin.Context) { } func UserCheckJoinedRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) id, err := middlewares.GetRoomIDFromContext(ctx) if err != nil { @@ -318,8 +334,8 @@ func UserCheckJoinedRoom(ctx *gin.Context) { } func SetUsername(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.SetUsernameReq if err := model.Decode(ctx, &req); err != nil { @@ -339,8 +355,8 @@ func SetUsername(ctx *gin.Context) { } func SetUserPassword(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.SetUserPasswordReq if err := model.Decode(ctx, &req); err != nil { @@ -360,8 +376,8 @@ func SetUserPassword(ctx *gin.Context) { } func UserBindProviders(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) up, err := db.GetBindProviders(user.ID) if err != nil { @@ -386,7 +402,7 @@ func UserBindProviders(ctx *gin.Context) { } } - m.Range(func(p provider.OAuth2Provider, pi struct{}) bool { + m.Range(func(p provider.OAuth2Provider, _ struct{}) bool { if _, ok := resp[p]; !ok { resp[p] = struct { ProviderUserID string `json:"providerUserId"` @@ -403,8 +419,8 @@ func UserBindProviders(ctx *gin.Context) { } func GetUserBindEmailStep1Captcha(ctx *gin.Context) { - // user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + // user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) id, data, _, err := captcha.Captcha.Generate() if err != nil { @@ -420,8 +436,8 @@ func GetUserBindEmailStep1Captcha(ctx *gin.Context) { } func SendUserBindEmailCaptcha(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.UserSendBindEmailCaptchaReq{} if err := model.Decode(ctx, &req); err != nil { @@ -436,19 +452,28 @@ func SendUserBindEmailCaptcha(ctx *gin.Context) { true, ) { log.Errorf("captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("captcha verify failed"), + ) return } if user.Email.String() == req.Email { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("this email same as current email")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("this email same as current email"), + ) return } _, err := op.LoadOrInitUserByEmail(req.Email) if err == nil { log.Errorf("email already bind") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email already bind")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email already bind"), + ) return } @@ -462,8 +487,8 @@ func SendUserBindEmailCaptcha(ctx *gin.Context) { } func UserBindEmail(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) req := model.UserBindEmailReq{} if err := model.Decode(ctx, &req); err != nil { @@ -474,7 +499,10 @@ func UserBindEmail(ctx *gin.Context) { if ok, err := user.VerifyBindCaptchaEmail(req.Email, req.Captcha); err != nil || !ok { log.Errorf("email captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email captcha verify failed"), + ) return } @@ -489,8 +517,8 @@ func UserBindEmail(ctx *gin.Context) { } func UserUnbindEmail(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) err := user.UnbindEmail() if err != nil { @@ -503,7 +531,7 @@ func UserUnbindEmail(ctx *gin.Context) { } func GetUserSignupEmailStep1Captcha(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) id, data, _, err := captcha.Captcha.Generate() if err != nil { @@ -519,11 +547,14 @@ func GetUserSignupEmailStep1Captcha(ctx *gin.Context) { } func SendUserSignupEmailCaptcha(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) if settings.DisableUserSignup.Get() { log.Errorf("user signup disabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user signup disabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user signup disabled"), + ) return } else if email.DisableUserSignup.Get() { log.Errorf("email signup disabled") @@ -544,7 +575,10 @@ func SendUserSignupEmailCaptcha(ctx *gin.Context) { true, ) { log.Errorf("captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("captcha verify failed"), + ) return } @@ -552,7 +586,10 @@ func SendUserSignupEmailCaptcha(ctx *gin.Context) { _, after, found := strings.Cut(req.Email, "@") if !found { log.Errorf("email format error") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email format error")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email format error"), + ) return } if !slices.Contains( @@ -560,7 +597,10 @@ func SendUserSignupEmailCaptcha(ctx *gin.Context) { after, ) { log.Errorf("email(%s) sub(%s) not in white list", req.Email, after) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email not in white list")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email not in white list"), + ) return } } @@ -568,7 +608,10 @@ func SendUserSignupEmailCaptcha(ctx *gin.Context) { _, err := op.LoadOrInitUserByEmail(req.Email) if err == nil { log.Errorf("email already exists") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email already exists")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email already exists"), + ) return } @@ -582,11 +625,14 @@ func SendUserSignupEmailCaptcha(ctx *gin.Context) { } func UserSignupEmail(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) if settings.DisableUserSignup.Get() { log.Errorf("user signup disabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user signup disabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user signup disabled"), + ) return } else if email.DisableUserSignup.Get() { log.Errorf("email signup disabled") @@ -609,13 +655,21 @@ func UserSignupEmail(ctx *gin.Context) { } if !ok { log.Errorf("email captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email captcha verify failed"), + ) return } var user *op.UserEntry if settings.SignupNeedReview.Get() || email.SignupNeedReview.Get() { - user, err = op.CreateUserWithEmail(req.Email, req.Password, req.Email, db.WithRole(dbModel.RolePending)) + user, err = op.CreateUserWithEmail( + req.Email, + req.Password, + req.Email, + db.WithRole(dbModel.RolePending), + ) } else { user, err = op.CreateUserWithEmail(req.Email, req.Password, req.Email) } @@ -629,7 +683,7 @@ func UserSignupEmail(ctx *gin.Context) { } func GetUserRetrievePasswordEmailStep1Captcha(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) id, data, _, err := captcha.Captcha.Generate() if err != nil { @@ -645,7 +699,7 @@ func GetUserRetrievePasswordEmailStep1Captcha(ctx *gin.Context) { } func SendUserRetrievePasswordEmailCaptcha(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.SendUserRetrievePasswordEmailCaptchaReq{} if err := model.Decode(ctx, &req); err != nil { @@ -660,7 +714,10 @@ func SendUserRetrievePasswordEmailCaptcha(ctx *gin.Context) { true, ) { log.Errorf("captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("captcha verify failed"), + ) return } @@ -681,7 +738,10 @@ func SendUserRetrievePasswordEmailCaptcha(ctx *gin.Context) { } if host == "" { log.Error("failed to get host on send retrieve password email") - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("failed to get host")) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("failed to get host"), + ) return } @@ -695,7 +755,7 @@ func SendUserRetrievePasswordEmailCaptcha(ctx *gin.Context) { } func UserRetrievePasswordEmail(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.UserRetrievePasswordEmailReq{} if err := model.Decode(ctx, &req); err != nil { @@ -712,9 +772,13 @@ func UserRetrievePasswordEmail(ctx *gin.Context) { } user := userE.Value() - if ok, err := user.VerifyRetrievePasswordCaptchaEmail(req.Email, req.Captcha); err != nil || !ok { + if ok, err := user.VerifyRetrievePasswordCaptchaEmail(req.Email, req.Captcha); err != nil || + !ok { log.Errorf("email captcha verify failed") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("email captcha verify failed")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("email captcha verify failed"), + ) return } @@ -729,8 +793,8 @@ func UserRetrievePasswordEmail(ctx *gin.Context) { } func UserDeleteRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.IDReq if err := model.Decode(ctx, &req); err != nil { @@ -765,11 +829,14 @@ func UserDeleteRoom(ctx *gin.Context) { } func UserSignupPassword(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) if settings.DisableUserSignup.Get() { log.Errorf("user signup disabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("user signup disabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("user signup disabled"), + ) return } else if !settings.EnablePasswordSignup.Get() { log.Errorf("password signup disabled") @@ -801,8 +868,8 @@ func UserSignupPassword(ctx *gin.Context) { } func UserExitRoom(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) var req model.IDReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/vendors/vendorAlist/alist.go b/server/handlers/vendors/vendorAlist/alist.go index 297d8554..f313f6f3 100644 --- a/server/handlers/vendors/vendorAlist/alist.go +++ b/server/handlers/vendors/vendorAlist/alist.go @@ -19,6 +19,7 @@ import ( "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/alist" @@ -31,7 +32,7 @@ type AlistVendorService struct { func NewAlistVendorService(room *op.Room, movie *op.Movie) (*AlistVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorAlist { - return nil, fmt.Errorf("alist vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("alist vendor not support vendor %s", movie.VendorInfo.Vendor) } return &AlistVendorService{ room: room, @@ -43,7 +44,13 @@ func (s *AlistVendorService) Client() alist.AlistHTTPServer { return vendor.LoadAlistClient(s.movie.VendorInfo.Backend) } -func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +//nolint:gosec +func (s *AlistVendorService) ListDynamicMovie( + ctx context.Context, + reqUser *op.User, + subPath, keyword string, + page, _max int, +) (*model.MovieList, error) { if reqUser.ID != s.movie.CreatorID { return nil, fmt.Errorf("list vendor dynamic folder error: %w", dbModel.ErrNoPermission) } @@ -83,26 +90,33 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U if err != nil { return nil, err } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Content)) - for i, flr := range data.Content { - fileSubPath := strings.TrimPrefix(strings.Trim(flr.Parent, "/"), truePath) + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetContent())) + for i, flr := range data.GetContent() { + fileSubPath := strings.TrimPrefix(strings.Trim(flr.GetParent(), "/"), truePath) resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", fileSubPath, flr.Name), "/"), + SubPath: "/" + strings.Trim( + fmt.Sprintf("%s/%s", fileSubPath, flr.GetName()), + "/", + ), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsDir, + Name: flr.GetName(), + IsFolder: flr.GetIsDir(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorAlist, Backend: s.movie.VendorInfo.Backend, Alist: &dbModel.AlistStreamingInfo{ - Path: dbModel.FormatAlistPath(serverID, - "/"+strings.Trim(fmt.Sprintf("%s/%s", flr.Parent, flr.Name), "/"), + Path: dbModel.FormatAlistPath( + serverID, + "/"+strings.Trim( + fmt.Sprintf("%s/%s", flr.GetParent(), flr.GetName()), + "/", + ), ), }, }, @@ -125,25 +139,25 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U if err != nil { return nil, err } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Content)) - for i, flr := range data.Content { + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetContent())) + for i, flr := range data.GetContent() { resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", subPath, flr.Name), "/"), + SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", subPath, flr.GetName()), "/"), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsDir, + Name: flr.GetName(), + IsFolder: flr.GetIsDir(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorAlist, Backend: s.movie.VendorInfo.Backend, Alist: &dbModel.AlistStreamingInfo{ Path: dbModel.FormatAlistPath(serverID, - "/"+strings.Trim(fmt.Sprintf("%s/%s", newPath, flr.Name), "/"), + "/"+strings.Trim(fmt.Sprintf("%s/%s", newPath, flr.GetName()), "/"), ), }, }, @@ -155,7 +169,7 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U } func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) // Get cache data data, err := s.getCacheData(ctx) @@ -177,7 +191,7 @@ func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { } func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { return nil, err } @@ -193,7 +207,11 @@ func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCa return data, nil } -func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliProvider( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { t := ctx.Query("t") switch t { case "": @@ -203,8 +221,15 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - if s.movie.Movie.MovieBase.Proxy { - err := proxy.M3u8Data(ctx, b.M3U8ListFile, "", ctx.GetString("token"), s.movie.RoomID, s.movie.ID) + if s.movie.Proxy { + err := proxy.M3u8Data( + ctx, + b.M3U8ListFile, + "", + ctx.GetString("token"), + s.movie.RoomID, + s.movie.ID, + ) if err != nil { log.Errorf("proxy vendor movie error: %v", err) } @@ -218,7 +243,7 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - if s.movie.Movie.MovieBase.Proxy { + if s.movie.Proxy { s.proxyURL(ctx, log, b.URL) } else { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) @@ -229,14 +254,21 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent } } -func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleDefaultProvider( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { t := ctx.Query("t") switch t { case "subtitle": idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id is empty")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id is empty"), + ) return } @@ -249,7 +281,10 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus if id >= len(data.Subtitles) { log.Errorf("proxy vendor movie error: %v", "id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id out of range"), + ) return } @@ -263,9 +298,12 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) default: - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } s.proxyURL(ctx, log, data.URL) @@ -275,7 +313,7 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url string) { err := proxy.AutoProxyURL(ctx, url, - s.movie.MovieBase.Type, + s.movie.Type, nil, ctx.GetString("token"), s.movie.RoomID, @@ -287,7 +325,11 @@ func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url s } } -func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliSubtitle( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") @@ -310,13 +352,17 @@ func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Ent } var subtitle *cache.AlistSubtitle - if id < len(data.Subtitles) { + switch { + case id < len(data.Subtitles): subtitle = data.Subtitles[id] - } else if id < len(data.Subtitles)+len(ali.Subtitles) { + case id < len(data.Subtitles)+len(ali.Subtitles): subtitle = ali.Subtitles[id-len(data.Subtitles)] - } else { + default: log.Errorf("proxy vendor movie error: %v", "id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id out of range"), + ) return } @@ -330,7 +376,11 @@ func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Ent http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) } -func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *AlistVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -352,11 +402,17 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us } for i, subt := range data.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -367,19 +423,24 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us if err != nil { return nil, err } - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" rawStreamURL := data.URL subPath := s.movie.SubPath() var rawType string if subPath == "" { - rawType = utils.GetURLExtension(movie.MovieBase.VendorInfo.Alist.Path) + rawType = utils.GetURLExtension(movie.VendorInfo.Alist.Path) } else { rawType = utils.GetURLExtension(subPath) } - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.MoreSources = []*dbModel.MoreSource{ { Name: "raw", Type: rawType, @@ -388,11 +449,17 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us } for i, subt := range ali.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + len(data.Subtitles)+i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -405,24 +472,28 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us if err != nil { return nil, fmt.Errorf("refresh 115 movie cache error: %w", err) } - movie.MovieBase.URL = data.URL - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + movie.URL = data.URL + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) for _, subt := range data.Subtitles { - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ URL: subt.URL, Type: subt.Type, } } default: - movie.MovieBase.URL = data.URL + movie.URL = data.URL } - movie.MovieBase.VendorInfo.Alist.Password = "" + movie.VendorInfo.Alist.Password = "" return movie, nil } -func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *AlistVendorService) GenProxyMovieInfo( + ctx context.Context, + _ *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error @@ -440,11 +511,17 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use } for i, subt := range data.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -455,39 +532,65 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use if err != nil { return nil, err } - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - rawStreamURL := fmt.Sprintf("/api/room/movie/proxy/%s?t=raw&token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + rawStreamURL := fmt.Sprintf( + "/api/room/movie/proxy/%s?t=raw&token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.MoreSources = []*dbModel.MoreSource{ { Name: "raw", - Type: utils.GetURLExtension(movie.MovieBase.VendorInfo.Alist.Path), + Type: utils.GetURLExtension(movie.VendorInfo.Alist.Path), URL: rawStreamURL, }, } for i, subt := range ali.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + len(data.Subtitles)+i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } case cache.AlistProvider115: - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = utils.GetURLExtension(data.URL) + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = utils.GetURLExtension(data.URL) // TODO: proxy subtitle default: - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = utils.GetURLExtension(data.URL) + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = utils.GetURLExtension(data.URL) } - movie.MovieBase.VendorInfo.Alist.Password = "" + movie.VendorInfo.Alist.Password = "" return movie, nil } diff --git a/server/handlers/vendors/vendorAlist/list.go b/server/handlers/vendors/vendorAlist/list.go index baff13f8..cfafc344 100644 --- a/server/handlers/vendors/vendorAlist/list.go +++ b/server/handlers/vendors/vendorAlist/list.go @@ -10,8 +10,8 @@ import ( json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/alist" @@ -40,8 +40,9 @@ type AlistFileItem struct { type AlistFSListResp = model.VendorFSListResp[*AlistFileItem] +//nolint:gosec func List(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ListReq{} if err := model.Decode(ctx, &req); err != nil { @@ -73,7 +74,10 @@ func List(ctx *gin.Context) { ev, err := db.GetAlistVendors(user.ID, append(socpes, db.Paginate(page, size))...) if err != nil { if errors.Is(err, db.NotFoundError(db.ErrVendorNotFound)) { - ctx.JSON(http.StatusBadRequest, model.NewAPIErrorStringResp("alist server not found")) + ctx.JSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("alist server not found"), + ) return } ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -152,7 +156,7 @@ AlistFSListResp: req.Path = strings.Trim(req.Path, "/") resp := AlistFSListResp{ - Total: data.Total, + Total: data.GetTotal(), Paths: model.GenDefaultPaths(req.Path, true, &model.Path{ Name: "", @@ -163,14 +167,18 @@ AlistFSListResp: Path: aucd.ServerID + "/", }), } - for _, flr := range data.Content { + for _, flr := range data.GetContent() { resp.Items = append(resp.Items, &AlistFileItem{ Item: &model.Item{ - Name: flr.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, strings.Trim(fmt.Sprintf("%s/%s", flr.Parent, flr.Name), "/")), - IsDir: flr.IsDir, + Name: flr.GetName(), + Path: fmt.Sprintf( + "%s/%s", + aucd.ServerID, + strings.Trim(fmt.Sprintf("%s/%s", flr.GetParent(), flr.GetName()), "/"), + ), + IsDir: flr.GetIsDir(), }, - Size: flr.Size, + Size: flr.GetSize(), }) } @@ -194,7 +202,7 @@ AlistFSListResp: req.Path = strings.Trim(req.Path, "/") resp := AlistFSListResp{ - Total: data.Total, + Total: data.GetTotal(), Paths: model.GenDefaultPaths(req.Path, true, &model.Path{ Name: "", @@ -205,14 +213,18 @@ AlistFSListResp: Path: aucd.ServerID + "/", }), } - for _, flr := range data.Content { + for _, flr := range data.GetContent() { resp.Items = append(resp.Items, &AlistFileItem{ Item: &model.Item{ - Name: flr.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, strings.Trim(fmt.Sprintf("%s/%s", req.Path, flr.Name), "/")), - IsDir: flr.IsDir, + Name: flr.GetName(), + Path: fmt.Sprintf( + "%s/%s", + aucd.ServerID, + strings.Trim(fmt.Sprintf("%s/%s", req.Path, flr.GetName()), "/"), + ), + IsDir: flr.GetIsDir(), }, - Size: flr.Size, + Size: flr.GetSize(), }) } diff --git a/server/handlers/vendors/vendorAlist/login.go b/server/handlers/vendors/vendorAlist/login.go index 5ed86a29..7225a107 100644 --- a/server/handlers/vendors/vendorAlist/login.go +++ b/server/handlers/vendors/vendorAlist/login.go @@ -11,11 +11,10 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) @@ -49,7 +48,7 @@ func (r *LoginReq) Decode(ctx *gin.Context) error { } func Login(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := LoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -89,9 +88,10 @@ func Login(ctx *gin.Context) { return } - _, err = user.AlistCache().StoreOrRefreshWithDynamicFunc(ctx, data.ServerID, func(ctx context.Context, key string, args ...struct{}) (*cache.AlistUserCacheData, error) { - return data, nil - }) + _, err = user.AlistCache(). + StoreOrRefreshWithDynamicFunc(ctx, data.ServerID, func(_ context.Context, _ string, _ ...struct{}) (*cache.AlistUserCacheData, error) { + return data, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -101,8 +101,8 @@ func Login(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) - user := ctx.MustGet("user").(*op.UserEntry).Value() + log := middlewares.GetLogger(ctx) + user := middlewares.GetUserEntry(ctx).Value() var req model.ServerIDReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/vendors/vendorAlist/me.go b/server/handlers/vendors/vendorAlist/me.go index c54fa8c3..ca927d5d 100644 --- a/server/handlers/vendors/vendorAlist/me.go +++ b/server/handlers/vendors/vendorAlist/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/alist" ) @@ -15,11 +15,14 @@ import ( type AlistMeResp = model.VendorMeResp[*alist.MeResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() serverID := ctx.Query("serverID") if serverID == "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(errors.New("serverID is required"))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorResp(errors.New("serverID is required")), + ) return } @@ -54,7 +57,7 @@ type AlistBindsResp []*struct { } func Binds(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() ev, err := db.GetAlistVendors(user.ID) if err != nil { diff --git a/server/handlers/vendors/vendorBilibili/bilibili.go b/server/handlers/vendors/vendorBilibili/bilibili.go index 524c0501..3856709a 100644 --- a/server/handlers/vendors/vendorBilibili/bilibili.go +++ b/server/handlers/vendors/vendorBilibili/bilibili.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "maps" "net/http" "strconv" "time" @@ -16,11 +17,11 @@ import ( "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" "github.com/zijiren233/stream" - "golang.org/x/exp/maps" ) type BilibiliVendorService struct { @@ -30,7 +31,7 @@ type BilibiliVendorService struct { func NewBilibiliVendorService(room *op.Room, movie *op.Movie) (*BilibiliVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorBilibili { - return nil, fmt.Errorf("bilibili vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("bilibili vendor not support vendor %s", movie.VendorInfo.Vendor) } return &BilibiliVendorService{ room: room, @@ -42,14 +43,19 @@ func (s *BilibiliVendorService) Client() bilibili.BilibiliHTTPServer { return vendor.LoadBilibiliClient(s.movie.VendorInfo.Backend) } -func (s *BilibiliVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +func (s *BilibiliVendorService) ListDynamicMovie( + _ context.Context, + _ *op.User, + _, _ string, + _, _ int, +) (*model.MovieList, error) { return nil, errors.New("bilibili vendor not support list dynamic movie") } func (s *BilibiliVendorService) ProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) - if s.movie.MovieBase.Live { + if s.movie.Live { s.handleLiveProxy(ctx, log) return } @@ -84,20 +90,26 @@ func (s *BilibiliVendorService) handleLiveProxy(ctx *gin.Context, log *logrus.En } if len(data) == 0 { log.Error("proxy vendor movie error: live data is empty") - ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorStringResp("live data is empty")) + ctx.AbortWithStatusJSON( + http.StatusNotFound, + model.NewAPIErrorStringResp("live data is empty"), + ) return } ctx.Data(http.StatusOK, "application/vnd.apple.mpegurl", data) } func (s *BilibiliVendorService) handleVideoProxy(ctx *gin.Context, log *logrus.Entry, t string) { - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -120,13 +132,18 @@ func (s *BilibiliVendorService) handleVideoProxy(ctx *gin.Context, log *logrus.E s.handleStreamProxy(ctx, log, id, mpdC) } -func (s *BilibiliVendorService) handleMpdProxy(ctx *gin.Context, log *logrus.Entry, t string, mpdC *cache.BilibiliMpdCache) { +func (s *BilibiliVendorService) handleMpdProxy( + ctx *gin.Context, + log *logrus.Entry, + t string, + mpdC *cache.BilibiliMpdCache, +) { var mpd string var err error if t == "hevc" { - mpd, err = cache.BilibiliMpdToString(mpdC.HevcMpd, ctx.MustGet("token").(string)) + mpd, err = cache.BilibiliMpdToString(mpdC.HevcMpd, middlewares.GetToken(ctx)) } else { - mpd, err = cache.BilibiliMpdToString(mpdC.Mpd, ctx.MustGet("token").(string)) + mpd, err = cache.BilibiliMpdToString(mpdC.Mpd, middlewares.GetToken(ctx)) } if err != nil { log.Errorf("proxy vendor movie error: %v", err) @@ -136,7 +153,12 @@ func (s *BilibiliVendorService) handleMpdProxy(ctx *gin.Context, log *logrus.Ent ctx.Data(http.StatusOK, "application/dash+xml", stream.StringToBytes(mpd)) } -func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus.Entry, id string, mpdC *cache.BilibiliMpdCache) { +func (s *BilibiliVendorService) handleStreamProxy( + ctx *gin.Context, + log *logrus.Entry, + id string, + mpdC *cache.BilibiliMpdCache, +) { streamID, err := strconv.Atoi(id) if err != nil { log.Errorf("proxy vendor movie error: %v", err) @@ -145,7 +167,10 @@ func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus. } if streamID >= len(mpdC.URLs) { log.Errorf("proxy vendor movie error: %v", "stream id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("stream id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("stream id out of range"), + ) return } @@ -161,7 +186,7 @@ func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus. } func (s *BilibiliVendorService) getProxyHeaders() map[string]string { - headers := maps.Clone(s.movie.Movie.MovieBase.Headers) + headers := maps.Clone(s.movie.Headers) if headers == nil { headers = map[string]string{ "Referer": "https://www.bilibili.com", @@ -182,7 +207,7 @@ func (s *BilibiliVendorService) handleSubtitleProxy(ctx *gin.Context, log *logru return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -211,7 +236,11 @@ func (s *BilibiliVendorService) handleSubtitleProxy(ctx *gin.Context, log *logru ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorStringResp("subtitle not found")) } -func (s *BilibiliVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *BilibiliVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -223,49 +252,78 @@ func (s *BilibiliVendorService) GenMovieInfo(ctx context.Context, user *op.User, } bmc := s.movie.BilibiliCache() - if movie.MovieBase.Live { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - movie.MovieBase.StreamDanmu = fmt.Sprintf("/api/room/movie/danmu/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) + if movie.Live { + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + movie.StreamDanmu = fmt.Sprintf( + "/api/room/movie/danmu/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) return movie, nil } - movie.Danmu = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", movie.ID, userToken, movie.RoomID) + movie.Danmu = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) var str string - if movie.MovieBase.VendorInfo.Bilibili.Shared { + if movie.VendorInfo.Bilibili.Shared { var u *op.UserEntry u, err = op.LoadOrInitUserByID(movie.CreatorID) if err != nil { return nil, err } - str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore(ctx, movie.CreatorID, u.Value().BilibiliCache()) + str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore( + ctx, + movie.CreatorID, + u.Value().BilibiliCache(), + ) } else { str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore(ctx, user.ID, user.BilibiliCache()) } if err != nil { return nil, err } - movie.MovieBase.URL = str + movie.URL = str srt, err := bmc.Subtitle.Get(ctx, user.BilibiliCache()) if err != nil { return nil, err } for k := range srt { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) } - movie.MovieBase.Subtitles[k] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", movie.ID, k, userToken, movie.RoomID), + movie.Subtitles[k] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", + movie.ID, + k, + userToken, + movie.RoomID, + ), Type: "srt", } } return movie, nil } -func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *BilibiliVendorService) GenProxyMovieInfo( + ctx context.Context, + user *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error if movie.IsFolder { @@ -273,23 +331,48 @@ func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op. } bmc := s.movie.BilibiliCache() - if movie.MovieBase.Live { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - movie.MovieBase.StreamDanmu = fmt.Sprintf("/api/room/movie/danmu/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) + if movie.Live { + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + movie.StreamDanmu = fmt.Sprintf( + "/api/room/movie/danmu/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) return movie, nil } - movie.Danmu = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", movie.ID, userToken, movie.RoomID) + movie.Danmu = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "mpd" - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "mpd" + movie.MoreSources = []*dbModel.MoreSource{ { Name: "hevc", Type: "mpd", - URL: fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=hevc&roomId=%s", movie.ID, userToken, movie.RoomID), + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=hevc&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ), }, } srt, err := bmc.Subtitle.Get(ctx, user.BilibiliCache()) @@ -297,11 +380,17 @@ func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op. return nil, err } for k := range srt { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) } - movie.MovieBase.Subtitles[k] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", movie.ID, k, userToken, movie.RoomID), + movie.Subtitles[k] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", + movie.ID, + k, + userToken, + movie.RoomID, + ), Type: "srt", } } diff --git a/server/handlers/vendors/vendorBilibili/login.go b/server/handlers/vendors/vendorBilibili/login.go index b2560c27..45481975 100644 --- a/server/handlers/vendors/vendorBilibili/login.go +++ b/server/handlers/vendors/vendorBilibili/login.go @@ -7,12 +7,11 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -43,7 +42,7 @@ func (r *QRCodeLoginReq) Decode(ctx *gin.Context) error { } func LoginWithQR(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := QRCodeLoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -52,15 +51,16 @@ func LoginWithQR(ctx *gin.Context) { } backend := ctx.Query("backend") - resp, err := vendor.LoadBilibiliClient(backend).LoginWithQRCode(ctx, &bilibili.LoginWithQRCodeReq{ - Key: req.Key, - }) + resp, err := vendor.LoadBilibiliClient(backend). + LoginWithQRCode(ctx, &bilibili.LoginWithQRCodeReq{ + Key: req.Key, + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - switch resp.Status { + switch resp.GetStatus() { case bilibili.QRCodeStatus_EXPIRED: ctx.JSON(http.StatusOK, model.NewAPIDataResp(gin.H{ "status": "expired", @@ -79,19 +79,21 @@ func LoginWithQR(ctx *gin.Context) { case bilibili.QRCodeStatus_SUCCESS: _, err = db.CreateOrSaveBilibiliVendor(&dbModel.BilibiliVendor{ UserID: user.ID, - Cookies: resp.Cookies, + Cookies: resp.GetCookies(), Backend: backend, }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.BilibiliCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.BilibiliUserCacheData, error) { - return &cache.BilibiliUserCacheData{ - Backend: backend, - Cookies: utils.MapToHTTPCookie(resp.Cookies), - }, nil - }) + _, err = user.BilibiliCache(). + Data(). + Refresh(ctx, func(_ context.Context, _ ...struct{}) (*cache.BilibiliUserCacheData, error) { + return &cache.BilibiliUserCacheData{ + Backend: backend, + Cookies: utils.MapToHTTPCookie(resp.GetCookies()), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -100,7 +102,10 @@ func LoginWithQR(ctx *gin.Context) { "status": "success", })) default: - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("unknown status")) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("unknown status"), + ) return } } @@ -159,7 +164,7 @@ func NewSMS(ctx *gin.Context) { return } ctx.JSON(http.StatusOK, model.NewAPIDataResp(gin.H{ - "captchaKey": r.CaptchaKey, + "captchaKey": r.GetCaptchaKey(), })) } @@ -187,7 +192,7 @@ func (r *SMSLoginReq) Decode(ctx *gin.Context) error { } func LoginWithSMS(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() var req SMSLoginReq if err := model.Decode(ctx, &req); err != nil { @@ -208,18 +213,20 @@ func LoginWithSMS(ctx *gin.Context) { _, err = db.CreateOrSaveBilibiliVendor(&dbModel.BilibiliVendor{ UserID: user.ID, Backend: backend, - Cookies: c.Cookies, + Cookies: c.GetCookies(), }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.BilibiliCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.BilibiliUserCacheData, error) { - return &cache.BilibiliUserCacheData{ - Backend: backend, - Cookies: utils.MapToHTTPCookie(c.Cookies), - }, nil - }) + _, err = user.BilibiliCache(). + Data(). + Refresh(ctx, func(_ context.Context, _ ...struct{}) (*cache.BilibiliUserCacheData, error) { + return &cache.BilibiliUserCacheData{ + Backend: backend, + Cookies: utils.MapToHTTPCookie(c.GetCookies()), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -228,8 +235,8 @@ func LoginWithSMS(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) - user := ctx.MustGet("user").(*op.UserEntry).Value() + log := middlewares.GetLogger(ctx) + user := middlewares.GetUserEntry(ctx).Value() err := db.DeleteBilibiliVendor(user.ID) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) diff --git a/server/handlers/vendors/vendorBilibili/me.go b/server/handlers/vendors/vendorBilibili/me.go index 278ac2fb..1f8f7c45 100644 --- a/server/handlers/vendors/vendorBilibili/me.go +++ b/server/handlers/vendors/vendorBilibili/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -16,7 +16,7 @@ import ( type BilibiliMeResp = model.VendorMeResp[*bilibili.UserInfoResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() bucd, err := user.BilibiliCache().Get(ctx) if err != nil { @@ -44,7 +44,7 @@ func Me(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(&BilibiliMeResp{ - IsLogin: resp.IsLogin, + IsLogin: resp.GetIsLogin(), Info: resp, })) } diff --git a/server/handlers/vendors/vendorBilibili/parse.go b/server/handlers/vendors/vendorBilibili/parse.go index 9f42c366..4e768ac1 100644 --- a/server/handlers/vendors/vendorBilibili/parse.go +++ b/server/handlers/vendors/vendorBilibili/parse.go @@ -8,8 +8,8 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -31,7 +31,7 @@ func (r *ParseReq) Decode(ctx *gin.Context) error { } func Parse(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ParseReq{} if err := model.Decode(ctx, &req); err != nil { @@ -61,11 +61,11 @@ func Parse(ctx *gin.Context) { cookies = bucd.Cookies } - switch resp.Type { + switch resp.GetType() { case "bv": resp, err := cli.ParseVideoPage(ctx, &bilibili.ParseVideoPageReq{ Cookies: utils.HTTPCookieToMap(cookies), - Bvid: resp.Id, + Bvid: resp.GetId(), Sections: ctx.DefaultQuery("sections", "false") == "true", }) if err != nil { @@ -74,7 +74,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "av": - aid, err := strconv.ParseUint(resp.Id, 10, 64) + aid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -90,7 +90,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "ep": - epid, err := strconv.ParseUint(resp.Id, 10, 64) + epid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -105,7 +105,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "ss": - ssid, err := strconv.ParseUint(resp.Id, 10, 64) + ssid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -120,7 +120,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "live": - roomid, err := strconv.ParseUint(resp.Id, 10, 64) + roomid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -135,7 +135,10 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) default: - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("unknown match type "+resp.Type)) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("unknown match type "+resp.GetType()), + ) return } } diff --git a/server/handlers/vendors/vendorEmby/emby.go b/server/handlers/vendors/vendorEmby/emby.go index ee734d06..93cc1128 100644 --- a/server/handlers/vendors/vendorEmby/emby.go +++ b/server/handlers/vendors/vendorEmby/emby.go @@ -11,12 +11,12 @@ import ( "time" "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/emby" @@ -29,7 +29,7 @@ type EmbyVendorService struct { func NewEmbyVendorService(room *op.Room, movie *op.Movie) (*EmbyVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorEmby { - return nil, fmt.Errorf("emby vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("emby vendor not support vendor %s", movie.VendorInfo.Vendor) } return &EmbyVendorService{ room: room, @@ -41,7 +41,13 @@ func (s *EmbyVendorService) Client() emby.EmbyHTTPServer { return vendor.LoadEmbyClient(s.movie.VendorInfo.Backend) } -func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +//nolint:gosec +func (s *EmbyVendorService) ListDynamicMovie( + ctx context.Context, + reqUser *op.User, + subPath, keyword string, + page, _max int, +) (*model.MovieList, error) { if reqUser.ID != s.movie.CreatorID { return nil, fmt.Errorf("list vendor dynamic folder error: %w", dbModel.ErrNoPermission) } @@ -77,24 +83,24 @@ func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.Us if err != nil { return nil, fmt.Errorf("emby fs list error: %w", err) } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Items)) - for i, flr := range data.Items { + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetItems())) + for i, flr := range data.GetItems() { resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: flr.Id, + SubPath: flr.GetId(), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsFolder, + Name: flr.GetName(), + IsFolder: flr.GetIsFolder(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorEmby, Backend: s.movie.VendorInfo.Backend, Emby: &dbModel.EmbyStreamingInfo{ - Path: dbModel.FormatEmbyPath(serverID, flr.Id), + Path: dbModel.FormatEmbyPath(serverID, flr.GetId()), }, }, }, @@ -104,15 +110,18 @@ func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.Us } func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp(err.Error())) @@ -141,7 +150,10 @@ func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { if source >= len(embyC.Sources) { log.Errorf("proxy vendor movie error: %v", "source out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("source out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("source out of range"), + ) return } @@ -179,7 +191,7 @@ func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { } func (s *EmbyVendorService) handleSubtitle(ctx *gin.Context) error { - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { return err } @@ -212,7 +224,13 @@ func (s *EmbyVendorService) handleSubtitle(ctx *gin.Context) error { return err } - http.ServeContent(ctx.Writer, ctx.Request, embyC.Sources[source].Subtitles[id].Name, time.Now(), bytes.NewReader(data)) + http.ServeContent( + ctx.Writer, + ctx.Request, + embyC.Sources[source].Subtitles[id].Name, + time.Now(), + bytes.NewReader(data), + ) return nil } @@ -221,13 +239,20 @@ func (s *EmbyVendorService) ProxyMovie(ctx *gin.Context) { case "": s.handleProxyMovie(ctx) case "subtitle": - s.handleSubtitle(ctx) + _ = s.handleSubtitle(ctx) default: - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp(fmt.Sprintf("unknown proxy type: %s", t))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("unknown proxy type: "+t), + ) } } -func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *EmbyVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -247,18 +272,18 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use if len(data.Sources) == 0 { return nil, errors.New("no source") } - movie.MovieBase.URL = data.Sources[0].URL + movie.URL = data.Sources[0].URL for _, s := range data.Sources[0].Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Sources[0].Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Sources[0].Subtitles)) } - movie.MovieBase.Subtitles[s.Name] = &dbModel.Subtitle{ + movie.Subtitles[s.Name] = &dbModel.Subtitle{ URL: s.URL, Type: s.Type, } } for _, s := range data.Sources[1:] { - movie.MovieBase.MoreSources = append(movie.MovieBase.MoreSources, + movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: s.Name, URL: s.URL, @@ -266,10 +291,10 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use ) for _, subt := range s.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(s.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(s.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ URL: subt.URL, Type: subt.Type, } @@ -279,7 +304,11 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use return movie, nil } -func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *EmbyVendorService) GenProxyMovieInfo( + ctx context.Context, + _ *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error @@ -297,7 +326,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User if si != len(data.Sources)-1 { continue } - if movie.MovieBase.URL == "" { + if movie.URL == "" { return nil, errors.New("no source") } } @@ -316,10 +345,10 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User } if si == 0 { - movie.MovieBase.URL = u.String() - movie.MovieBase.Type = utils.GetURLExtension(es.URL) + movie.URL = u.String() + movie.Type = utils.GetURLExtension(es.URL) } else { - movie.MovieBase.MoreSources = append(movie.MovieBase.MoreSources, + movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: es.Name, URL: u.String(), @@ -332,8 +361,8 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User continue } for sbi, s := range es.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(es.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(es.Subtitles)) } rawQuery := url.Values{} rawQuery.Set("t", "subtitle") @@ -345,7 +374,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User Path: rawPath, RawQuery: rawQuery.Encode(), } - movie.MovieBase.Subtitles[s.Name] = &dbModel.Subtitle{ + movie.Subtitles[s.Name] = &dbModel.Subtitle{ URL: u.String(), Type: s.Type, } diff --git a/server/handlers/vendors/vendorEmby/list.go b/server/handlers/vendors/vendorEmby/list.go index 4faf75de..960ad206 100644 --- a/server/handlers/vendors/vendorEmby/list.go +++ b/server/handlers/vendors/vendorEmby/list.go @@ -9,8 +9,8 @@ import ( json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/emby" @@ -37,8 +37,9 @@ type EmbyFileItem struct { type EmbyFSListResp = model.VendorFSListResp[*EmbyFileItem] +//nolint:gosec func List(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ListReq{} if err := model.Decode(ctx, &req); err != nil { @@ -54,7 +55,12 @@ func List(ctx *gin.Context) { if req.Path == "" { if req.Keyword != "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("keywords is not supported when not choose server (server id is empty)")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp( + "keywords is not supported when not choose server (server id is empty)", + ), + ) return } socpes := [](func(*gorm.DB) *gorm.DB){ @@ -74,7 +80,10 @@ func List(ctx *gin.Context) { ev, err := db.GetEmbyVendors(user.ID, append(socpes, db.Paginate(page, size))...) if err != nil { if errors.Is(err, db.NotFoundError(db.ErrVendorNotFound)) { - ctx.JSON(http.StatusBadRequest, model.NewAPIErrorStringResp("emby server not found")) + ctx.JSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("emby server not found"), + ) return } ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -142,36 +151,39 @@ EmbyFSListResp: SearchTerm: req.Keyword, }) if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(fmt.Errorf("emby fs list error: %w", err))) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(fmt.Errorf("emby fs list error: %w", err)), + ) return } - var resp EmbyFSListResp = EmbyFSListResp{ + resp := EmbyFSListResp{ Paths: []*model.Path{ {}, }, } - for _, p := range data.Paths { - n := p.Name - if p.Path == "1" { + for _, p := range data.GetPaths() { + n := p.GetName() + if p.GetPath() == "1" { n = aucd.Host } resp.Paths = append(resp.Paths, &model.Path{ Name: n, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, p.Path), + Path: fmt.Sprintf("%s/%s", aucd.ServerID, p.GetPath()), }) } - for _, i := range data.Items { + for _, i := range data.GetItems() { resp.Items = append(resp.Items, &EmbyFileItem{ Item: &model.Item{ - Name: i.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, i.Id), - IsDir: i.IsFolder, + Name: i.GetName(), + Path: fmt.Sprintf("%s/%s", aucd.ServerID, i.GetId()), + IsDir: i.GetIsFolder(), }, - Type: i.Type, + Type: i.GetType(), }) } - resp.Total = data.Total + resp.Total = data.GetTotal() ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) } diff --git a/server/handlers/vendors/vendorEmby/login.go b/server/handlers/vendors/vendorEmby/login.go index 6eb421cf..1ad44de8 100644 --- a/server/handlers/vendors/vendorEmby/login.go +++ b/server/handlers/vendors/vendorEmby/login.go @@ -12,8 +12,8 @@ import ( "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/emby" ) @@ -47,7 +47,7 @@ func (r *LoginReq) Decode(ctx *gin.Context) error { } func Login(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := LoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -68,33 +68,37 @@ func Login(ctx *gin.Context) { return } - if data.ServerId == "" { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("serverID is empty")) + if data.GetServerId() == "" { + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("serverID is empty"), + ) return } _, err = db.CreateOrSaveEmbyVendor(&dbModel.EmbyVendor{ UserID: user.ID, - ServerID: data.ServerId, + ServerID: data.GetServerId(), Host: req.Host, - APIKey: data.Token, + APIKey: data.GetToken(), Backend: backend, - EmbyUserID: data.UserId, + EmbyUserID: data.GetUserId(), }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.EmbyCache().StoreOrRefreshWithDynamicFunc(ctx, data.ServerId, func(ctx context.Context, key string) (*cache.EmbyUserCacheData, error) { - return &cache.EmbyUserCacheData{ - Host: req.Host, - ServerID: key, - APIKey: data.Token, - Backend: backend, - UserID: data.UserId, - }, nil - }) + _, err = user.EmbyCache(). + StoreOrRefreshWithDynamicFunc(ctx, data.GetServerId(), func(_ context.Context, key string) (*cache.EmbyUserCacheData, error) { + return &cache.EmbyUserCacheData{ + Host: req.Host, + ServerID: key, + APIKey: data.GetToken(), + Backend: backend, + UserID: data.GetUserId(), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -104,7 +108,7 @@ func Login(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() var req model.ServerIDReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/vendors/vendorEmby/me.go b/server/handlers/vendors/vendorEmby/me.go index 6a49d3ff..79a5fafc 100644 --- a/server/handlers/vendors/vendorEmby/me.go +++ b/server/handlers/vendors/vendorEmby/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/emby" ) @@ -15,11 +15,14 @@ import ( type EmbyMeResp = model.VendorMeResp[*emby.SystemInfoResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() serverID := ctx.Query("serverID") if serverID == "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(errors.New("serverID is required"))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorResp(errors.New("serverID is required")), + ) return } @@ -54,7 +57,7 @@ type EmbyBindsResp []*struct { } func Binds(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() ev, err := db.GetEmbyVendors(user.ID) if err != nil { diff --git a/server/handlers/vendors/vendoralist/alist.go b/server/handlers/vendors/vendoralist/alist.go index 297d8554..f313f6f3 100644 --- a/server/handlers/vendors/vendoralist/alist.go +++ b/server/handlers/vendors/vendoralist/alist.go @@ -19,6 +19,7 @@ import ( "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/alist" @@ -31,7 +32,7 @@ type AlistVendorService struct { func NewAlistVendorService(room *op.Room, movie *op.Movie) (*AlistVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorAlist { - return nil, fmt.Errorf("alist vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("alist vendor not support vendor %s", movie.VendorInfo.Vendor) } return &AlistVendorService{ room: room, @@ -43,7 +44,13 @@ func (s *AlistVendorService) Client() alist.AlistHTTPServer { return vendor.LoadAlistClient(s.movie.VendorInfo.Backend) } -func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +//nolint:gosec +func (s *AlistVendorService) ListDynamicMovie( + ctx context.Context, + reqUser *op.User, + subPath, keyword string, + page, _max int, +) (*model.MovieList, error) { if reqUser.ID != s.movie.CreatorID { return nil, fmt.Errorf("list vendor dynamic folder error: %w", dbModel.ErrNoPermission) } @@ -83,26 +90,33 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U if err != nil { return nil, err } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Content)) - for i, flr := range data.Content { - fileSubPath := strings.TrimPrefix(strings.Trim(flr.Parent, "/"), truePath) + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetContent())) + for i, flr := range data.GetContent() { + fileSubPath := strings.TrimPrefix(strings.Trim(flr.GetParent(), "/"), truePath) resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", fileSubPath, flr.Name), "/"), + SubPath: "/" + strings.Trim( + fmt.Sprintf("%s/%s", fileSubPath, flr.GetName()), + "/", + ), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsDir, + Name: flr.GetName(), + IsFolder: flr.GetIsDir(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorAlist, Backend: s.movie.VendorInfo.Backend, Alist: &dbModel.AlistStreamingInfo{ - Path: dbModel.FormatAlistPath(serverID, - "/"+strings.Trim(fmt.Sprintf("%s/%s", flr.Parent, flr.Name), "/"), + Path: dbModel.FormatAlistPath( + serverID, + "/"+strings.Trim( + fmt.Sprintf("%s/%s", flr.GetParent(), flr.GetName()), + "/", + ), ), }, }, @@ -125,25 +139,25 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U if err != nil { return nil, err } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Content)) - for i, flr := range data.Content { + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetContent())) + for i, flr := range data.GetContent() { resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", subPath, flr.Name), "/"), + SubPath: "/" + strings.Trim(fmt.Sprintf("%s/%s", subPath, flr.GetName()), "/"), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsDir, + Name: flr.GetName(), + IsFolder: flr.GetIsDir(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorAlist, Backend: s.movie.VendorInfo.Backend, Alist: &dbModel.AlistStreamingInfo{ Path: dbModel.FormatAlistPath(serverID, - "/"+strings.Trim(fmt.Sprintf("%s/%s", newPath, flr.Name), "/"), + "/"+strings.Trim(fmt.Sprintf("%s/%s", newPath, flr.GetName()), "/"), ), }, }, @@ -155,7 +169,7 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U } func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) // Get cache data data, err := s.getCacheData(ctx) @@ -177,7 +191,7 @@ func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { } func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { return nil, err } @@ -193,7 +207,11 @@ func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCa return data, nil } -func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliProvider( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { t := ctx.Query("t") switch t { case "": @@ -203,8 +221,15 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - if s.movie.Movie.MovieBase.Proxy { - err := proxy.M3u8Data(ctx, b.M3U8ListFile, "", ctx.GetString("token"), s.movie.RoomID, s.movie.ID) + if s.movie.Proxy { + err := proxy.M3u8Data( + ctx, + b.M3U8ListFile, + "", + ctx.GetString("token"), + s.movie.RoomID, + s.movie.ID, + ) if err != nil { log.Errorf("proxy vendor movie error: %v", err) } @@ -218,7 +243,7 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - if s.movie.Movie.MovieBase.Proxy { + if s.movie.Proxy { s.proxyURL(ctx, log, b.URL) } else { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) @@ -229,14 +254,21 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent } } -func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleDefaultProvider( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { t := ctx.Query("t") switch t { case "subtitle": idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id is empty")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id is empty"), + ) return } @@ -249,7 +281,10 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus if id >= len(data.Subtitles) { log.Errorf("proxy vendor movie error: %v", "id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id out of range"), + ) return } @@ -263,9 +298,12 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) default: - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } s.proxyURL(ctx, log, data.URL) @@ -275,7 +313,7 @@ func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url string) { err := proxy.AutoProxyURL(ctx, url, - s.movie.MovieBase.Type, + s.movie.Type, nil, ctx.GetString("token"), s.movie.RoomID, @@ -287,7 +325,11 @@ func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url s } } -func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliSubtitle( + ctx *gin.Context, + log *logrus.Entry, + data *cache.AlistMovieCacheData, +) { idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") @@ -310,13 +352,17 @@ func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Ent } var subtitle *cache.AlistSubtitle - if id < len(data.Subtitles) { + switch { + case id < len(data.Subtitles): subtitle = data.Subtitles[id] - } else if id < len(data.Subtitles)+len(ali.Subtitles) { + case id < len(data.Subtitles)+len(ali.Subtitles): subtitle = ali.Subtitles[id-len(data.Subtitles)] - } else { + default: log.Errorf("proxy vendor movie error: %v", "id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("id out of range"), + ) return } @@ -330,7 +376,11 @@ func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Ent http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) } -func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *AlistVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -352,11 +402,17 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us } for i, subt := range data.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -367,19 +423,24 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us if err != nil { return nil, err } - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" rawStreamURL := data.URL subPath := s.movie.SubPath() var rawType string if subPath == "" { - rawType = utils.GetURLExtension(movie.MovieBase.VendorInfo.Alist.Path) + rawType = utils.GetURLExtension(movie.VendorInfo.Alist.Path) } else { rawType = utils.GetURLExtension(subPath) } - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.MoreSources = []*dbModel.MoreSource{ { Name: "raw", Type: rawType, @@ -388,11 +449,17 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us } for i, subt := range ali.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + len(data.Subtitles)+i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -405,24 +472,28 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us if err != nil { return nil, fmt.Errorf("refresh 115 movie cache error: %w", err) } - movie.MovieBase.URL = data.URL - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + movie.URL = data.URL + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) for _, subt := range data.Subtitles { - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ URL: subt.URL, Type: subt.Type, } } default: - movie.MovieBase.URL = data.URL + movie.URL = data.URL } - movie.MovieBase.VendorInfo.Alist.Password = "" + movie.VendorInfo.Alist.Password = "" return movie, nil } -func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *AlistVendorService) GenProxyMovieInfo( + ctx context.Context, + _ *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error @@ -440,11 +511,17 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use } for i, subt := range data.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } @@ -455,39 +532,65 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use if err != nil { return nil, err } - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - rawStreamURL := fmt.Sprintf("/api/room/movie/proxy/%s?t=raw&token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + rawStreamURL := fmt.Sprintf( + "/api/room/movie/proxy/%s?t=raw&token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.MoreSources = []*dbModel.MoreSource{ { Name: "raw", - Type: utils.GetURLExtension(movie.MovieBase.VendorInfo.Alist.Path), + Type: utils.GetURLExtension(movie.VendorInfo.Alist.Path), URL: rawStreamURL, }, } for i, subt := range ali.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", + movie.ID, + len(data.Subtitles)+i, + userToken, + movie.RoomID, + ), Type: subt.Type, } } case cache.AlistProvider115: - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = utils.GetURLExtension(data.URL) + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = utils.GetURLExtension(data.URL) // TODO: proxy subtitle default: - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = utils.GetURLExtension(data.URL) + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = utils.GetURLExtension(data.URL) } - movie.MovieBase.VendorInfo.Alist.Password = "" + movie.VendorInfo.Alist.Password = "" return movie, nil } diff --git a/server/handlers/vendors/vendoralist/list.go b/server/handlers/vendors/vendoralist/list.go index baff13f8..cfafc344 100644 --- a/server/handlers/vendors/vendoralist/list.go +++ b/server/handlers/vendors/vendoralist/list.go @@ -10,8 +10,8 @@ import ( json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/alist" @@ -40,8 +40,9 @@ type AlistFileItem struct { type AlistFSListResp = model.VendorFSListResp[*AlistFileItem] +//nolint:gosec func List(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ListReq{} if err := model.Decode(ctx, &req); err != nil { @@ -73,7 +74,10 @@ func List(ctx *gin.Context) { ev, err := db.GetAlistVendors(user.ID, append(socpes, db.Paginate(page, size))...) if err != nil { if errors.Is(err, db.NotFoundError(db.ErrVendorNotFound)) { - ctx.JSON(http.StatusBadRequest, model.NewAPIErrorStringResp("alist server not found")) + ctx.JSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("alist server not found"), + ) return } ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -152,7 +156,7 @@ AlistFSListResp: req.Path = strings.Trim(req.Path, "/") resp := AlistFSListResp{ - Total: data.Total, + Total: data.GetTotal(), Paths: model.GenDefaultPaths(req.Path, true, &model.Path{ Name: "", @@ -163,14 +167,18 @@ AlistFSListResp: Path: aucd.ServerID + "/", }), } - for _, flr := range data.Content { + for _, flr := range data.GetContent() { resp.Items = append(resp.Items, &AlistFileItem{ Item: &model.Item{ - Name: flr.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, strings.Trim(fmt.Sprintf("%s/%s", flr.Parent, flr.Name), "/")), - IsDir: flr.IsDir, + Name: flr.GetName(), + Path: fmt.Sprintf( + "%s/%s", + aucd.ServerID, + strings.Trim(fmt.Sprintf("%s/%s", flr.GetParent(), flr.GetName()), "/"), + ), + IsDir: flr.GetIsDir(), }, - Size: flr.Size, + Size: flr.GetSize(), }) } @@ -194,7 +202,7 @@ AlistFSListResp: req.Path = strings.Trim(req.Path, "/") resp := AlistFSListResp{ - Total: data.Total, + Total: data.GetTotal(), Paths: model.GenDefaultPaths(req.Path, true, &model.Path{ Name: "", @@ -205,14 +213,18 @@ AlistFSListResp: Path: aucd.ServerID + "/", }), } - for _, flr := range data.Content { + for _, flr := range data.GetContent() { resp.Items = append(resp.Items, &AlistFileItem{ Item: &model.Item{ - Name: flr.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, strings.Trim(fmt.Sprintf("%s/%s", req.Path, flr.Name), "/")), - IsDir: flr.IsDir, + Name: flr.GetName(), + Path: fmt.Sprintf( + "%s/%s", + aucd.ServerID, + strings.Trim(fmt.Sprintf("%s/%s", req.Path, flr.GetName()), "/"), + ), + IsDir: flr.GetIsDir(), }, - Size: flr.Size, + Size: flr.GetSize(), }) } diff --git a/server/handlers/vendors/vendoralist/login.go b/server/handlers/vendors/vendoralist/login.go index 5ed86a29..7225a107 100644 --- a/server/handlers/vendors/vendoralist/login.go +++ b/server/handlers/vendors/vendoralist/login.go @@ -11,11 +11,10 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) @@ -49,7 +48,7 @@ func (r *LoginReq) Decode(ctx *gin.Context) error { } func Login(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := LoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -89,9 +88,10 @@ func Login(ctx *gin.Context) { return } - _, err = user.AlistCache().StoreOrRefreshWithDynamicFunc(ctx, data.ServerID, func(ctx context.Context, key string, args ...struct{}) (*cache.AlistUserCacheData, error) { - return data, nil - }) + _, err = user.AlistCache(). + StoreOrRefreshWithDynamicFunc(ctx, data.ServerID, func(_ context.Context, _ string, _ ...struct{}) (*cache.AlistUserCacheData, error) { + return data, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -101,8 +101,8 @@ func Login(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) - user := ctx.MustGet("user").(*op.UserEntry).Value() + log := middlewares.GetLogger(ctx) + user := middlewares.GetUserEntry(ctx).Value() var req model.ServerIDReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/vendors/vendoralist/me.go b/server/handlers/vendors/vendoralist/me.go index c54fa8c3..ca927d5d 100644 --- a/server/handlers/vendors/vendoralist/me.go +++ b/server/handlers/vendors/vendoralist/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/alist" ) @@ -15,11 +15,14 @@ import ( type AlistMeResp = model.VendorMeResp[*alist.MeResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() serverID := ctx.Query("serverID") if serverID == "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(errors.New("serverID is required"))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorResp(errors.New("serverID is required")), + ) return } @@ -54,7 +57,7 @@ type AlistBindsResp []*struct { } func Binds(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() ev, err := db.GetAlistVendors(user.ID) if err != nil { diff --git a/server/handlers/vendors/vendorbilibili/bilibili.go b/server/handlers/vendors/vendorbilibili/bilibili.go index 524c0501..3856709a 100644 --- a/server/handlers/vendors/vendorbilibili/bilibili.go +++ b/server/handlers/vendors/vendorbilibili/bilibili.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "maps" "net/http" "strconv" "time" @@ -16,11 +17,11 @@ import ( "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" "github.com/zijiren233/stream" - "golang.org/x/exp/maps" ) type BilibiliVendorService struct { @@ -30,7 +31,7 @@ type BilibiliVendorService struct { func NewBilibiliVendorService(room *op.Room, movie *op.Movie) (*BilibiliVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorBilibili { - return nil, fmt.Errorf("bilibili vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("bilibili vendor not support vendor %s", movie.VendorInfo.Vendor) } return &BilibiliVendorService{ room: room, @@ -42,14 +43,19 @@ func (s *BilibiliVendorService) Client() bilibili.BilibiliHTTPServer { return vendor.LoadBilibiliClient(s.movie.VendorInfo.Backend) } -func (s *BilibiliVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +func (s *BilibiliVendorService) ListDynamicMovie( + _ context.Context, + _ *op.User, + _, _ string, + _, _ int, +) (*model.MovieList, error) { return nil, errors.New("bilibili vendor not support list dynamic movie") } func (s *BilibiliVendorService) ProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) - if s.movie.MovieBase.Live { + if s.movie.Live { s.handleLiveProxy(ctx, log) return } @@ -84,20 +90,26 @@ func (s *BilibiliVendorService) handleLiveProxy(ctx *gin.Context, log *logrus.En } if len(data) == 0 { log.Error("proxy vendor movie error: live data is empty") - ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorStringResp("live data is empty")) + ctx.AbortWithStatusJSON( + http.StatusNotFound, + model.NewAPIErrorStringResp("live data is empty"), + ) return } ctx.Data(http.StatusOK, "application/vnd.apple.mpegurl", data) } func (s *BilibiliVendorService) handleVideoProxy(ctx *gin.Context, log *logrus.Entry, t string) { - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -120,13 +132,18 @@ func (s *BilibiliVendorService) handleVideoProxy(ctx *gin.Context, log *logrus.E s.handleStreamProxy(ctx, log, id, mpdC) } -func (s *BilibiliVendorService) handleMpdProxy(ctx *gin.Context, log *logrus.Entry, t string, mpdC *cache.BilibiliMpdCache) { +func (s *BilibiliVendorService) handleMpdProxy( + ctx *gin.Context, + log *logrus.Entry, + t string, + mpdC *cache.BilibiliMpdCache, +) { var mpd string var err error if t == "hevc" { - mpd, err = cache.BilibiliMpdToString(mpdC.HevcMpd, ctx.MustGet("token").(string)) + mpd, err = cache.BilibiliMpdToString(mpdC.HevcMpd, middlewares.GetToken(ctx)) } else { - mpd, err = cache.BilibiliMpdToString(mpdC.Mpd, ctx.MustGet("token").(string)) + mpd, err = cache.BilibiliMpdToString(mpdC.Mpd, middlewares.GetToken(ctx)) } if err != nil { log.Errorf("proxy vendor movie error: %v", err) @@ -136,7 +153,12 @@ func (s *BilibiliVendorService) handleMpdProxy(ctx *gin.Context, log *logrus.Ent ctx.Data(http.StatusOK, "application/dash+xml", stream.StringToBytes(mpd)) } -func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus.Entry, id string, mpdC *cache.BilibiliMpdCache) { +func (s *BilibiliVendorService) handleStreamProxy( + ctx *gin.Context, + log *logrus.Entry, + id string, + mpdC *cache.BilibiliMpdCache, +) { streamID, err := strconv.Atoi(id) if err != nil { log.Errorf("proxy vendor movie error: %v", err) @@ -145,7 +167,10 @@ func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus. } if streamID >= len(mpdC.URLs) { log.Errorf("proxy vendor movie error: %v", "stream id out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("stream id out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("stream id out of range"), + ) return } @@ -161,7 +186,7 @@ func (s *BilibiliVendorService) handleStreamProxy(ctx *gin.Context, log *logrus. } func (s *BilibiliVendorService) getProxyHeaders() map[string]string { - headers := maps.Clone(s.movie.Movie.MovieBase.Headers) + headers := maps.Clone(s.movie.Headers) if headers == nil { headers = map[string]string{ "Referer": "https://www.bilibili.com", @@ -182,7 +207,7 @@ func (s *BilibiliVendorService) handleSubtitleProxy(ctx *gin.Context, log *logru return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -211,7 +236,11 @@ func (s *BilibiliVendorService) handleSubtitleProxy(ctx *gin.Context, log *logru ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewAPIErrorStringResp("subtitle not found")) } -func (s *BilibiliVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *BilibiliVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -223,49 +252,78 @@ func (s *BilibiliVendorService) GenMovieInfo(ctx context.Context, user *op.User, } bmc := s.movie.BilibiliCache() - if movie.MovieBase.Live { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - movie.MovieBase.StreamDanmu = fmt.Sprintf("/api/room/movie/danmu/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) + if movie.Live { + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + movie.StreamDanmu = fmt.Sprintf( + "/api/room/movie/danmu/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) return movie, nil } - movie.Danmu = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", movie.ID, userToken, movie.RoomID) + movie.Danmu = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) var str string - if movie.MovieBase.VendorInfo.Bilibili.Shared { + if movie.VendorInfo.Bilibili.Shared { var u *op.UserEntry u, err = op.LoadOrInitUserByID(movie.CreatorID) if err != nil { return nil, err } - str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore(ctx, movie.CreatorID, u.Value().BilibiliCache()) + str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore( + ctx, + movie.CreatorID, + u.Value().BilibiliCache(), + ) } else { str, err = s.movie.BilibiliCache().NoSharedMovie.LoadOrStore(ctx, user.ID, user.BilibiliCache()) } if err != nil { return nil, err } - movie.MovieBase.URL = str + movie.URL = str srt, err := bmc.Subtitle.Get(ctx, user.BilibiliCache()) if err != nil { return nil, err } for k := range srt { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) } - movie.MovieBase.Subtitles[k] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", movie.ID, k, userToken, movie.RoomID), + movie.Subtitles[k] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", + movie.ID, + k, + userToken, + movie.RoomID, + ), Type: "srt", } } return movie, nil } -func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *BilibiliVendorService) GenProxyMovieInfo( + ctx context.Context, + user *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error if movie.IsFolder { @@ -273,23 +331,48 @@ func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op. } bmc := s.movie.BilibiliCache() - if movie.MovieBase.Live { - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "m3u8" - - movie.MovieBase.StreamDanmu = fmt.Sprintf("/api/room/movie/danmu/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) + if movie.Live { + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "m3u8" + + movie.StreamDanmu = fmt.Sprintf( + "/api/room/movie/danmu/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) return movie, nil } - movie.Danmu = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", movie.ID, userToken, movie.RoomID) + movie.Danmu = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=danmu&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) - movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) - movie.MovieBase.Type = "mpd" - movie.MovieBase.MoreSources = []*dbModel.MoreSource{ + movie.URL = fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ) + movie.Type = "mpd" + movie.MoreSources = []*dbModel.MoreSource{ { Name: "hevc", Type: "mpd", - URL: fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&t=hevc&roomId=%s", movie.ID, userToken, movie.RoomID), + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?token=%s&t=hevc&roomId=%s", + movie.ID, + userToken, + movie.RoomID, + ), }, } srt, err := bmc.Subtitle.Get(ctx, user.BilibiliCache()) @@ -297,11 +380,17 @@ func (s *BilibiliVendorService) GenProxyMovieInfo(ctx context.Context, user *op. return nil, err } for k := range srt { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(srt)) } - movie.MovieBase.Subtitles[k] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", movie.ID, k, userToken, movie.RoomID), + movie.Subtitles[k] = &dbModel.Subtitle{ + URL: fmt.Sprintf( + "/api/room/movie/proxy/%s?t=subtitle&n=%s&token=%s&roomId=%s", + movie.ID, + k, + userToken, + movie.RoomID, + ), Type: "srt", } } diff --git a/server/handlers/vendors/vendorbilibili/danmu.go b/server/handlers/vendors/vendorbilibili/danmu.go index a6e6b91e..1144525b 100644 --- a/server/handlers/vendors/vendorbilibili/danmu.go +++ b/server/handlers/vendors/vendorbilibili/danmu.go @@ -8,7 +8,9 @@ import ( "errors" "fmt" "io" + "net" "net/http" + "strconv" "time" "github.com/andybalholm/brotli" @@ -23,11 +25,11 @@ import ( type command uint32 const ( - CMD_HEARTBEAT command = 2 - CMD_HEARTBEAT_REPLY command = 3 - CMD_NORMAL command = 5 - CMD_AUTH command = 7 - CMD_AUTH_REPLY command = 8 + CmdHeartbeat command = 2 + CmdHeartbeatReply command = 3 + CmdNormal command = 5 + CmdAuth command = 7 + CmdAuthReply command = 8 ) type header struct { @@ -53,6 +55,7 @@ func (h *header) Unmarshal(data []byte) error { return binary.Read(bytes.NewReader(data), binary.BigEndian, h) } +//nolint:gosec func newHeader(size uint32, command command, sequence uint32) header { h := header{ TotalSize: uint32(headerLen) + size, @@ -61,7 +64,7 @@ func newHeader(size uint32, command command, sequence uint32) header { Sequence: sequence, } switch command { - case CMD_HEARTBEAT, CMD_AUTH: + case CmdHeartbeat, CmdAuth: h.Version = 1 } return h @@ -86,12 +89,13 @@ func newVerifyHello(roomID uint64, key string) *verifyHello { } } +//nolint:gosec func writeVerifyHello(conn *websocket.Conn, hello *verifyHello) error { msg, err := json.Marshal(hello) if err != nil { return err } - header := newHeader(uint32(len(msg)), CMD_AUTH, 1) + header := newHeader(uint32(len(msg)), CmdAuth, 1) headerBytes, err := header.Marshal() if err != nil { return err @@ -100,7 +104,7 @@ func writeVerifyHello(conn *websocket.Conn, hello *verifyHello) error { } func writeHeartbeat(conn *websocket.Conn, sequence uint32) error { - header := newHeader(0, CMD_HEARTBEAT, sequence) + header := newHeader(0, CmdHeartbeat, sequence) headerBytes, err := header.Marshal() if err != nil { return err @@ -112,24 +116,27 @@ type replyCmd struct { Cmd string `json:"cmd"` } -func (v *BilibiliVendorService) StreamDanmu(ctx context.Context, handler func(danmu string) error) error { +func (v *BilibiliVendorService) StreamDanmu( + ctx context.Context, + handler func(danmu string) error, +) error { resp, err := vendor.LoadBilibiliClient("").GetLiveDanmuInfo(ctx, &bilibili.GetLiveDanmuInfoReq{ RoomID: v.movie.VendorInfo.Bilibili.Cid, }) if err != nil { return err } - if len(resp.HostList) == 0 { + if len(resp.GetHostList()) == 0 { return errors.New("no host list") } - wssHost := resp.HostList[0].Host - wssPort := resp.HostList[0].WssPort + wssHost := resp.GetHostList()[0].GetHost() + wssPort := resp.GetHostList()[0].GetWssPort() - conn, _, err := websocket. + conn, wsresp, err := websocket. DefaultDialer. DialContext( ctx, - fmt.Sprintf("wss://%s:%d/sub", wssHost, wssPort), + fmt.Sprintf("wss://%s/sub", net.JoinHostPort(wssHost, strconv.Itoa(int(wssPort)))), http.Header{ "User-Agent": []string{utils.UA}, "Origin": []string{"https://live.bilibili.com"}, @@ -139,12 +146,13 @@ func (v *BilibiliVendorService) StreamDanmu(ctx context.Context, handler func(da return err } defer conn.Close() + defer wsresp.Body.Close() err = writeVerifyHello( conn, newVerifyHello( v.movie.VendorInfo.Bilibili.Cid, - resp.Token, + resp.GetToken(), ), ) if err != nil { @@ -189,8 +197,9 @@ func (v *BilibiliVendorService) StreamDanmu(ctx context.Context, handler func(da return err } switch header.Command { - case CMD_HEARTBEAT_REPLY: + case CmdHeartbeatReply: continue + default: } data := message[headerLen:] switch header.Version { @@ -230,7 +239,7 @@ func (v *BilibiliVendorService) StreamDanmu(ctx context.Context, handler func(da if !ok { return errors.New("content is not string") } - handler(content) + _ = handler(content) case "DM_INTERACTION": } } diff --git a/server/handlers/vendors/vendorbilibili/login.go b/server/handlers/vendors/vendorbilibili/login.go index b2560c27..45481975 100644 --- a/server/handlers/vendors/vendorbilibili/login.go +++ b/server/handlers/vendors/vendorbilibili/login.go @@ -7,12 +7,11 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -43,7 +42,7 @@ func (r *QRCodeLoginReq) Decode(ctx *gin.Context) error { } func LoginWithQR(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := QRCodeLoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -52,15 +51,16 @@ func LoginWithQR(ctx *gin.Context) { } backend := ctx.Query("backend") - resp, err := vendor.LoadBilibiliClient(backend).LoginWithQRCode(ctx, &bilibili.LoginWithQRCodeReq{ - Key: req.Key, - }) + resp, err := vendor.LoadBilibiliClient(backend). + LoginWithQRCode(ctx, &bilibili.LoginWithQRCodeReq{ + Key: req.Key, + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - switch resp.Status { + switch resp.GetStatus() { case bilibili.QRCodeStatus_EXPIRED: ctx.JSON(http.StatusOK, model.NewAPIDataResp(gin.H{ "status": "expired", @@ -79,19 +79,21 @@ func LoginWithQR(ctx *gin.Context) { case bilibili.QRCodeStatus_SUCCESS: _, err = db.CreateOrSaveBilibiliVendor(&dbModel.BilibiliVendor{ UserID: user.ID, - Cookies: resp.Cookies, + Cookies: resp.GetCookies(), Backend: backend, }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.BilibiliCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.BilibiliUserCacheData, error) { - return &cache.BilibiliUserCacheData{ - Backend: backend, - Cookies: utils.MapToHTTPCookie(resp.Cookies), - }, nil - }) + _, err = user.BilibiliCache(). + Data(). + Refresh(ctx, func(_ context.Context, _ ...struct{}) (*cache.BilibiliUserCacheData, error) { + return &cache.BilibiliUserCacheData{ + Backend: backend, + Cookies: utils.MapToHTTPCookie(resp.GetCookies()), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -100,7 +102,10 @@ func LoginWithQR(ctx *gin.Context) { "status": "success", })) default: - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("unknown status")) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("unknown status"), + ) return } } @@ -159,7 +164,7 @@ func NewSMS(ctx *gin.Context) { return } ctx.JSON(http.StatusOK, model.NewAPIDataResp(gin.H{ - "captchaKey": r.CaptchaKey, + "captchaKey": r.GetCaptchaKey(), })) } @@ -187,7 +192,7 @@ func (r *SMSLoginReq) Decode(ctx *gin.Context) error { } func LoginWithSMS(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() var req SMSLoginReq if err := model.Decode(ctx, &req); err != nil { @@ -208,18 +213,20 @@ func LoginWithSMS(ctx *gin.Context) { _, err = db.CreateOrSaveBilibiliVendor(&dbModel.BilibiliVendor{ UserID: user.ID, Backend: backend, - Cookies: c.Cookies, + Cookies: c.GetCookies(), }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.BilibiliCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.BilibiliUserCacheData, error) { - return &cache.BilibiliUserCacheData{ - Backend: backend, - Cookies: utils.MapToHTTPCookie(c.Cookies), - }, nil - }) + _, err = user.BilibiliCache(). + Data(). + Refresh(ctx, func(_ context.Context, _ ...struct{}) (*cache.BilibiliUserCacheData, error) { + return &cache.BilibiliUserCacheData{ + Backend: backend, + Cookies: utils.MapToHTTPCookie(c.GetCookies()), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -228,8 +235,8 @@ func LoginWithSMS(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) - user := ctx.MustGet("user").(*op.UserEntry).Value() + log := middlewares.GetLogger(ctx) + user := middlewares.GetUserEntry(ctx).Value() err := db.DeleteBilibiliVendor(user.ID) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) diff --git a/server/handlers/vendors/vendorbilibili/me.go b/server/handlers/vendors/vendorbilibili/me.go index 278ac2fb..1f8f7c45 100644 --- a/server/handlers/vendors/vendorbilibili/me.go +++ b/server/handlers/vendors/vendorbilibili/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -16,7 +16,7 @@ import ( type BilibiliMeResp = model.VendorMeResp[*bilibili.UserInfoResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() bucd, err := user.BilibiliCache().Get(ctx) if err != nil { @@ -44,7 +44,7 @@ func Me(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(&BilibiliMeResp{ - IsLogin: resp.IsLogin, + IsLogin: resp.GetIsLogin(), Info: resp, })) } diff --git a/server/handlers/vendors/vendorbilibili/parse.go b/server/handlers/vendors/vendorbilibili/parse.go index 9f42c366..4e768ac1 100644 --- a/server/handlers/vendors/vendorbilibili/parse.go +++ b/server/handlers/vendors/vendorbilibili/parse.go @@ -8,8 +8,8 @@ import ( "github.com/gin-gonic/gin" json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/bilibili" @@ -31,7 +31,7 @@ func (r *ParseReq) Decode(ctx *gin.Context) error { } func Parse(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ParseReq{} if err := model.Decode(ctx, &req); err != nil { @@ -61,11 +61,11 @@ func Parse(ctx *gin.Context) { cookies = bucd.Cookies } - switch resp.Type { + switch resp.GetType() { case "bv": resp, err := cli.ParseVideoPage(ctx, &bilibili.ParseVideoPageReq{ Cookies: utils.HTTPCookieToMap(cookies), - Bvid: resp.Id, + Bvid: resp.GetId(), Sections: ctx.DefaultQuery("sections", "false") == "true", }) if err != nil { @@ -74,7 +74,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "av": - aid, err := strconv.ParseUint(resp.Id, 10, 64) + aid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -90,7 +90,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "ep": - epid, err := strconv.ParseUint(resp.Id, 10, 64) + epid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -105,7 +105,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "ss": - ssid, err := strconv.ParseUint(resp.Id, 10, 64) + ssid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -120,7 +120,7 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) case "live": - roomid, err := strconv.ParseUint(resp.Id, 10, 64) + roomid, err := strconv.ParseUint(resp.GetId(), 10, 64) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -135,7 +135,10 @@ func Parse(ctx *gin.Context) { } ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) default: - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("unknown match type "+resp.Type)) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("unknown match type "+resp.GetType()), + ) return } } diff --git a/server/handlers/vendors/vendoremby/emby.go b/server/handlers/vendors/vendoremby/emby.go index ee734d06..93cc1128 100644 --- a/server/handlers/vendors/vendoremby/emby.go +++ b/server/handlers/vendors/vendoremby/emby.go @@ -11,12 +11,12 @@ import ( "time" "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" "github.com/synctv-org/synctv/server/handlers/proxy" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/emby" @@ -29,7 +29,7 @@ type EmbyVendorService struct { func NewEmbyVendorService(room *op.Room, movie *op.Movie) (*EmbyVendorService, error) { if movie.VendorInfo.Vendor != dbModel.VendorEmby { - return nil, fmt.Errorf("emby vendor not support vendor %s", movie.MovieBase.VendorInfo.Vendor) + return nil, fmt.Errorf("emby vendor not support vendor %s", movie.VendorInfo.Vendor) } return &EmbyVendorService{ room: room, @@ -41,7 +41,13 @@ func (s *EmbyVendorService) Client() emby.EmbyHTTPServer { return vendor.LoadEmbyClient(s.movie.VendorInfo.Backend) } -func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) { +//nolint:gosec +func (s *EmbyVendorService) ListDynamicMovie( + ctx context.Context, + reqUser *op.User, + subPath, keyword string, + page, _max int, +) (*model.MovieList, error) { if reqUser.ID != s.movie.CreatorID { return nil, fmt.Errorf("list vendor dynamic folder error: %w", dbModel.ErrNoPermission) } @@ -77,24 +83,24 @@ func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.Us if err != nil { return nil, fmt.Errorf("emby fs list error: %w", err) } - resp.Total = int64(data.Total) - resp.Movies = make([]*model.Movie, len(data.Items)) - for i, flr := range data.Items { + resp.Total = int64(data.GetTotal()) + resp.Movies = make([]*model.Movie, len(data.GetItems())) + for i, flr := range data.GetItems() { resp.Movies[i] = &model.Movie{ ID: s.movie.ID, CreatedAt: s.movie.CreatedAt.UnixMilli(), Creator: op.GetUserName(s.movie.CreatorID), CreatorID: s.movie.CreatorID, - SubPath: flr.Id, + SubPath: flr.GetId(), Base: dbModel.MovieBase{ - Name: flr.Name, - IsFolder: flr.IsFolder, + Name: flr.GetName(), + IsFolder: flr.GetIsFolder(), ParentID: dbModel.EmptyNullString(s.movie.ID), VendorInfo: dbModel.VendorInfo{ Vendor: dbModel.VendorEmby, Backend: s.movie.VendorInfo.Backend, Emby: &dbModel.EmbyStreamingInfo{ - Path: dbModel.FormatEmbyPath(serverID, flr.Id), + Path: dbModel.FormatEmbyPath(serverID, flr.GetId()), }, }, }, @@ -104,15 +110,18 @@ func (s *EmbyVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.Us } func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { - log := ctx.MustGet("log").(*log.Entry) + log := middlewares.GetLogger(ctx) - if !s.movie.Movie.MovieBase.Proxy { + if !s.movie.Proxy { log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("proxy is not enabled"), + ) return } - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp(err.Error())) @@ -141,7 +150,10 @@ func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { if source >= len(embyC.Sources) { log.Errorf("proxy vendor movie error: %v", "source out of range") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("source out of range")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("source out of range"), + ) return } @@ -179,7 +191,7 @@ func (s *EmbyVendorService) handleProxyMovie(ctx *gin.Context) { } func (s *EmbyVendorService) handleSubtitle(ctx *gin.Context) error { - u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) + u, err := op.LoadOrInitUserByID(s.movie.CreatorID) if err != nil { return err } @@ -212,7 +224,13 @@ func (s *EmbyVendorService) handleSubtitle(ctx *gin.Context) error { return err } - http.ServeContent(ctx.Writer, ctx.Request, embyC.Sources[source].Subtitles[id].Name, time.Now(), bytes.NewReader(data)) + http.ServeContent( + ctx.Writer, + ctx.Request, + embyC.Sources[source].Subtitles[id].Name, + time.Now(), + bytes.NewReader(data), + ) return nil } @@ -221,13 +239,20 @@ func (s *EmbyVendorService) ProxyMovie(ctx *gin.Context) { case "": s.handleProxyMovie(ctx) case "subtitle": - s.handleSubtitle(ctx) + _ = s.handleSubtitle(ctx) default: - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp(fmt.Sprintf("unknown proxy type: %s", t))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("unknown proxy type: "+t), + ) } } -func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *EmbyVendorService) GenMovieInfo( + ctx context.Context, + user *op.User, + userAgent, userToken string, +) (*dbModel.Movie, error) { if s.movie.Proxy { return s.GenProxyMovieInfo(ctx, user, userAgent, userToken) } @@ -247,18 +272,18 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use if len(data.Sources) == 0 { return nil, errors.New("no source") } - movie.MovieBase.URL = data.Sources[0].URL + movie.URL = data.Sources[0].URL for _, s := range data.Sources[0].Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Sources[0].Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Sources[0].Subtitles)) } - movie.MovieBase.Subtitles[s.Name] = &dbModel.Subtitle{ + movie.Subtitles[s.Name] = &dbModel.Subtitle{ URL: s.URL, Type: s.Type, } } for _, s := range data.Sources[1:] { - movie.MovieBase.MoreSources = append(movie.MovieBase.MoreSources, + movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: s.Name, URL: s.URL, @@ -266,10 +291,10 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use ) for _, subt := range s.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(s.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(s.Subtitles)) } - movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ + movie.Subtitles[subt.Name] = &dbModel.Subtitle{ URL: subt.URL, Type: subt.Type, } @@ -279,7 +304,11 @@ func (s *EmbyVendorService) GenMovieInfo(ctx context.Context, user *op.User, use return movie, nil } -func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { +func (s *EmbyVendorService) GenProxyMovieInfo( + ctx context.Context, + _ *op.User, + _, userToken string, +) (*dbModel.Movie, error) { movie := s.movie.Clone() var err error @@ -297,7 +326,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User if si != len(data.Sources)-1 { continue } - if movie.MovieBase.URL == "" { + if movie.URL == "" { return nil, errors.New("no source") } } @@ -316,10 +345,10 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User } if si == 0 { - movie.MovieBase.URL = u.String() - movie.MovieBase.Type = utils.GetURLExtension(es.URL) + movie.URL = u.String() + movie.Type = utils.GetURLExtension(es.URL) } else { - movie.MovieBase.MoreSources = append(movie.MovieBase.MoreSources, + movie.MoreSources = append(movie.MoreSources, &dbModel.MoreSource{ Name: es.Name, URL: u.String(), @@ -332,8 +361,8 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User continue } for sbi, s := range es.Subtitles { - if movie.MovieBase.Subtitles == nil { - movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(es.Subtitles)) + if movie.Subtitles == nil { + movie.Subtitles = make(map[string]*dbModel.Subtitle, len(es.Subtitles)) } rawQuery := url.Values{} rawQuery.Set("t", "subtitle") @@ -345,7 +374,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User Path: rawPath, RawQuery: rawQuery.Encode(), } - movie.MovieBase.Subtitles[s.Name] = &dbModel.Subtitle{ + movie.Subtitles[s.Name] = &dbModel.Subtitle{ URL: u.String(), Type: s.Type, } diff --git a/server/handlers/vendors/vendoremby/list.go b/server/handlers/vendors/vendoremby/list.go index 4faf75de..960ad206 100644 --- a/server/handlers/vendors/vendoremby/list.go +++ b/server/handlers/vendors/vendoremby/list.go @@ -9,8 +9,8 @@ import ( json "github.com/json-iterator/go" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" "github.com/synctv-org/vendors/api/emby" @@ -37,8 +37,9 @@ type EmbyFileItem struct { type EmbyFSListResp = model.VendorFSListResp[*EmbyFileItem] +//nolint:gosec func List(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := ListReq{} if err := model.Decode(ctx, &req); err != nil { @@ -54,7 +55,12 @@ func List(ctx *gin.Context) { if req.Path == "" { if req.Keyword != "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("keywords is not supported when not choose server (server id is empty)")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp( + "keywords is not supported when not choose server (server id is empty)", + ), + ) return } socpes := [](func(*gorm.DB) *gorm.DB){ @@ -74,7 +80,10 @@ func List(ctx *gin.Context) { ev, err := db.GetEmbyVendors(user.ID, append(socpes, db.Paginate(page, size))...) if err != nil { if errors.Is(err, db.NotFoundError(db.ErrVendorNotFound)) { - ctx.JSON(http.StatusBadRequest, model.NewAPIErrorStringResp("emby server not found")) + ctx.JSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("emby server not found"), + ) return } ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -142,36 +151,39 @@ EmbyFSListResp: SearchTerm: req.Keyword, }) if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(fmt.Errorf("emby fs list error: %w", err))) + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(fmt.Errorf("emby fs list error: %w", err)), + ) return } - var resp EmbyFSListResp = EmbyFSListResp{ + resp := EmbyFSListResp{ Paths: []*model.Path{ {}, }, } - for _, p := range data.Paths { - n := p.Name - if p.Path == "1" { + for _, p := range data.GetPaths() { + n := p.GetName() + if p.GetPath() == "1" { n = aucd.Host } resp.Paths = append(resp.Paths, &model.Path{ Name: n, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, p.Path), + Path: fmt.Sprintf("%s/%s", aucd.ServerID, p.GetPath()), }) } - for _, i := range data.Items { + for _, i := range data.GetItems() { resp.Items = append(resp.Items, &EmbyFileItem{ Item: &model.Item{ - Name: i.Name, - Path: fmt.Sprintf("%s/%s", aucd.ServerID, i.Id), - IsDir: i.IsFolder, + Name: i.GetName(), + Path: fmt.Sprintf("%s/%s", aucd.ServerID, i.GetId()), + IsDir: i.GetIsFolder(), }, - Type: i.Type, + Type: i.GetType(), }) } - resp.Total = data.Total + resp.Total = data.GetTotal() ctx.JSON(http.StatusOK, model.NewAPIDataResp(resp)) } diff --git a/server/handlers/vendors/vendoremby/login.go b/server/handlers/vendors/vendoremby/login.go index 6eb421cf..1ad44de8 100644 --- a/server/handlers/vendors/vendoremby/login.go +++ b/server/handlers/vendors/vendoremby/login.go @@ -12,8 +12,8 @@ import ( "github.com/synctv-org/synctv/internal/cache" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/emby" ) @@ -47,7 +47,7 @@ func (r *LoginReq) Decode(ctx *gin.Context) error { } func Login(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() req := LoginReq{} if err := model.Decode(ctx, &req); err != nil { @@ -68,33 +68,37 @@ func Login(ctx *gin.Context) { return } - if data.ServerId == "" { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorStringResp("serverID is empty")) + if data.GetServerId() == "" { + ctx.AbortWithStatusJSON( + http.StatusInternalServerError, + model.NewAPIErrorStringResp("serverID is empty"), + ) return } _, err = db.CreateOrSaveEmbyVendor(&dbModel.EmbyVendor{ UserID: user.ID, - ServerID: data.ServerId, + ServerID: data.GetServerId(), Host: req.Host, - APIKey: data.Token, + APIKey: data.GetToken(), Backend: backend, - EmbyUserID: data.UserId, + EmbyUserID: data.GetUserId(), }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - _, err = user.EmbyCache().StoreOrRefreshWithDynamicFunc(ctx, data.ServerId, func(ctx context.Context, key string) (*cache.EmbyUserCacheData, error) { - return &cache.EmbyUserCacheData{ - Host: req.Host, - ServerID: key, - APIKey: data.Token, - Backend: backend, - UserID: data.UserId, - }, nil - }) + _, err = user.EmbyCache(). + StoreOrRefreshWithDynamicFunc(ctx, data.GetServerId(), func(_ context.Context, key string) (*cache.EmbyUserCacheData, error) { + return &cache.EmbyUserCacheData{ + Host: req.Host, + ServerID: key, + APIKey: data.GetToken(), + Backend: backend, + UserID: data.GetUserId(), + }, nil + }) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return @@ -104,7 +108,7 @@ func Login(ctx *gin.Context) { } func Logout(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() var req model.ServerIDReq if err := model.Decode(ctx, &req); err != nil { diff --git a/server/handlers/vendors/vendoremby/me.go b/server/handlers/vendors/vendoremby/me.go index 6a49d3ff..79a5fafc 100644 --- a/server/handlers/vendors/vendoremby/me.go +++ b/server/handlers/vendors/vendoremby/me.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/vendor" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/vendors/api/emby" ) @@ -15,11 +15,14 @@ import ( type EmbyMeResp = model.VendorMeResp[*emby.SystemInfoResp] func Me(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() serverID := ctx.Query("serverID") if serverID == "" { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(errors.New("serverID is required"))) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorResp(errors.New("serverID is required")), + ) return } @@ -54,7 +57,7 @@ type EmbyBindsResp []*struct { } func Binds(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() + user := middlewares.GetUserEntry(ctx).Value() ev, err := db.GetEmbyVendors(user.ID) if err != nil { diff --git a/server/handlers/vendors/vendors.go b/server/handlers/vendors/vendors.go index 1e5fc979..84eec28f 100644 --- a/server/handlers/vendors/vendors.go +++ b/server/handlers/vendors/vendors.go @@ -3,7 +3,9 @@ package vendors import ( "context" "fmt" + "maps" "net/http" + "slices" "github.com/gin-gonic/gin" dbModel "github.com/synctv-org/synctv/internal/model" @@ -13,29 +15,40 @@ import ( "github.com/synctv-org/synctv/server/handlers/vendors/vendorbilibili" "github.com/synctv-org/synctv/server/handlers/vendors/vendoremby" "github.com/synctv-org/synctv/server/model" - "golang.org/x/exp/maps" ) func Backends(ctx *gin.Context) { var backends []string switch ctx.Param("vendor") { case dbModel.VendorBilibili: - backends = maps.Keys(vendor.LoadClients().BilibiliClients()) + backends = slices.Collect(maps.Keys(vendor.LoadClients().BilibiliClients())) case dbModel.VendorAlist: - backends = maps.Keys(vendor.LoadClients().AlistClients()) + backends = slices.Collect(maps.Keys(vendor.LoadClients().AlistClients())) case dbModel.VendorEmby: - backends = maps.Keys(vendor.LoadClients().EmbyClients()) + backends = slices.Collect(maps.Keys(vendor.LoadClients().EmbyClients())) default: - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid vendor name")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid vendor name"), + ) return } ctx.JSON(http.StatusOK, model.NewAPIDataResp(backends)) } type VendorService interface { - ListDynamicMovie(ctx context.Context, reqUser *op.User, subPath string, keyword string, page, _max int) (*model.MovieList, error) + ListDynamicMovie( + ctx context.Context, + reqUser *op.User, + subPath, keyword string, + page, _max int, + ) (*model.MovieList, error) ProxyMovie(ctx *gin.Context) - GenMovieInfo(ctx context.Context, reqUser *op.User, userAgent, userToken string) (*dbModel.Movie, error) + GenMovieInfo( + ctx context.Context, + reqUser *op.User, + userAgent, userToken string, + ) (*dbModel.Movie, error) } type VendorDanmuService interface { diff --git a/server/handlers/websocket.go b/server/handlers/websocket.go index f111b393..90157339 100644 --- a/server/handlers/websocket.go +++ b/server/handlers/websocket.go @@ -12,9 +12,9 @@ import ( "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/model" - dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" pb "github.com/synctv-org/synctv/proto/message" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/utils" "google.golang.org/protobuf/proto" ) @@ -26,10 +26,10 @@ const ( func NewWebSocketHandler(wss *utils.WebSocket) gin.HandlerFunc { return func(ctx *gin.Context) { - token := ctx.MustGet("token").(string) - room := ctx.MustGet("room").(*op.RoomEntry).Value() - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*log.Entry) + token := middlewares.GetToken(ctx) + room := middlewares.GetRoomEntry(ctx).Value() + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) subprotocols := []string{} if token != "" { @@ -198,9 +198,9 @@ func readMessage(c *op.Client) (*pb.Message, error) { } func handleElementMsg(cli *op.Client, msg *pb.Message) error { - timeDiff := calculateTimeDiff(msg.Timestamp) + timeDiff := calculateTimeDiff(msg.GetTimestamp()) - switch msg.Type { + switch msg.GetType() { case pb.MessageType_CHAT: return handleChatMessage(cli, msg.GetChatContent()) case pb.MessageType_STATUS: @@ -222,7 +222,7 @@ func handleElementMsg(cli *op.Client, msg *pb.Message) error { case pb.MessageType_WEBRTC_LEAVE: return handleWebRTCLeave(cli) default: - return sendErrorMessage(cli, fmt.Sprintf("unknown message type: %v", msg.Type)) + return sendErrorMessage(cli, fmt.Sprintf("unknown message type: %v", msg.GetType())) } } @@ -236,7 +236,7 @@ func handleWebRTCOffer(cli *op.Client, data *pb.WebRTCData) error { return sendErrorMessage(cli, "webrtc data is nil") } - sp := strings.Split(data.To, ":") + sp := strings.Split(data.GetTo(), ":") if len(sp) != 2 { return sendErrorMessage(cli, "target user id is invalid") } @@ -265,7 +265,7 @@ func handleWebRTCAnswer(cli *op.Client, data *pb.WebRTCData) error { return sendErrorMessage(cli, "webrtc data is nil") } - sp := strings.Split(data.To, ":") + sp := strings.Split(data.GetTo(), ":") if len(sp) != 2 { return sendErrorMessage(cli, "target user id is invalid") } @@ -294,7 +294,7 @@ func handleWebRTCIceCandidate(cli *op.Client, data *pb.WebRTCData) error { return sendErrorMessage(cli, "webrtc data is nil") } - sp := strings.Split(data.To, ":") + sp := strings.Split(data.GetTo(), ":") if len(sp) != 2 { return sendErrorMessage(cli, "target user id is invalid") } @@ -377,7 +377,7 @@ func handleChatMessage(cli *op.Client, message string) error { return sendErrorMessage(cli, "message too long") } err := cli.SendChatMessage(message) - if err != nil && errors.Is(err, dbModel.ErrNoPermission) { + if err != nil && errors.Is(err, model.ErrNoPermission) { return sendErrorMessage(cli, fmt.Sprintf("send chat message error: %v", err)) } return err @@ -451,10 +451,10 @@ func handleCheckStatusMessage(cli *op.Client, msg *pb.Message, timeDiff float64) } func needsSync(clientStatus *pb.Status, serverStatus model.Status, timeDiff float64) bool { - if clientStatus.IsPlaying != serverStatus.IsPlaying || - clientStatus.PlaybackRate != serverStatus.PlaybackRate || - serverStatus.CurrentTime+maxInterval < clientStatus.CurrentTime+timeDiff || - serverStatus.CurrentTime-maxInterval > clientStatus.CurrentTime+timeDiff { + if clientStatus.GetIsPlaying() != serverStatus.IsPlaying || + clientStatus.GetPlaybackRate() != serverStatus.PlaybackRate || + serverStatus.CurrentTime+maxInterval < clientStatus.GetCurrentTime()+timeDiff || + serverStatus.CurrentTime-maxInterval > clientStatus.GetCurrentTime()+timeDiff { return true } return false diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go index 16874cbb..d71c7afd 100644 --- a/server/middlewares/auth.go +++ b/server/middlewares/auth.go @@ -8,7 +8,6 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/conf" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -42,9 +41,13 @@ type AuthClaims struct { } func authUser(authorization string) (*AuthClaims, error) { - t, err := jwt.ParseWithClaims(strings.TrimPrefix(authorization, `Bearer `), &AuthClaims{}, func(token *jwt.Token) (any, error) { - return stream.StringToBytes(conf.Conf.Jwt.Secret), nil - }) + t, err := jwt.ParseWithClaims( + strings.TrimPrefix(authorization, `Bearer `), + &AuthClaims{}, + func(_ *jwt.Token) (any, error) { + return stream.StringToBytes(conf.Conf.Jwt.Secret), nil + }, + ) if err != nil || !t.Valid { return nil, ErrAuthFailed } @@ -236,7 +239,8 @@ func NewAuthUserToken(user *op.User) (string, error) { ExpiresAt: jwt.NewNumericDate(time.Now().Add(t)), }, } - return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) + return jwt.NewWithClaims(jwt.SigningMethodHS256, claims). + SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) } func validateNewAuthUserToken(user *op.User) error { @@ -294,7 +298,15 @@ func AuthRoomWithoutGuestMiddleware(ctx *gin.Context) { return } - user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid user type")), + ) + return + } + user := userEntry.Value() if user.IsGuest() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(ErrUserGuest)) return @@ -307,8 +319,24 @@ func AuthRoomAdminMiddleware(ctx *gin.Context) { return } - room := ctx.MustGet("room").(*synccache.Entry[*op.Room]).Value() - user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() + roomEntry, ok := ctx.MustGet("room").(*synccache.Entry[*op.Room]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid room type")), + ) + return + } + room := roomEntry.Value() + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid user type")), + ) + return + } + user := userEntry.Value() if !user.IsRoomAdmin(room) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(ErrNotRoomAdmin)) @@ -322,8 +350,24 @@ func AuthRoomCreatorMiddleware(ctx *gin.Context) { return } - room := ctx.MustGet("room").(*synccache.Entry[*op.Room]).Value() - user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() + roomEntry, ok := ctx.MustGet("room").(*synccache.Entry[*op.Room]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid room type")), + ) + return + } + room := roomEntry.Value() + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid user type")), + ) + return + } + user := userEntry.Value() if room.CreatorID != user.ID { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(ErrNotRoomCreator)) @@ -337,8 +381,16 @@ func AuthAdminMiddleware(ctx *gin.Context) { return } - userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) - if !userE.Value().IsAdmin() { + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid user type")), + ) + return + } + user := userEntry.Value() + if !user.IsAdmin() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(ErrNotAdmin)) return } @@ -350,8 +402,16 @@ func AuthRootMiddleware(ctx *gin.Context) { return } - userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) - if !userE.Value().IsRoot() { + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid user type")), + ) + return + } + user := userEntry.Value() + if !user.IsRoot() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewAPIErrorResp(ErrNotRoot)) return } @@ -405,7 +465,7 @@ func GetRoomIDFromContext(ctx *gin.Context) (string, error) { } func setLogFields(ctx *gin.Context, user *op.User, room *op.Room) { - log := ctx.MustGet("log").(*logrus.Entry) + log := GetLogger(ctx) if user != nil { log.Data["uid"] = user.ID log.Data["unm"] = user.Username @@ -416,3 +476,31 @@ func setLogFields(ctx *gin.Context, user *op.User, room *op.Room) { log.Data["rnm"] = room.Name } } + +func GetUserEntry(ctx *gin.Context) *op.UserEntry { + userEntry, ok := ctx.MustGet("user").(*synccache.Entry[*op.User]) + if !ok { + panic("invalid user type") + } + return userEntry +} + +func GetRoomEntry(ctx *gin.Context) *op.RoomEntry { + roomEntry, ok := ctx.MustGet("room").(*synccache.Entry[*op.Room]) + if !ok { + panic("invalid room type") + } + return roomEntry +} + +func GetToken(ctx *gin.Context) string { + token, ok := ctx.Get("token") + if !ok { + return "" + } + t, ok := token.(string) + if !ok { + panic("invalid token type") + } + return t +} diff --git a/server/middlewares/init.go b/server/middlewares/init.go index be708a61..733f9610 100644 --- a/server/middlewares/init.go +++ b/server/middlewares/init.go @@ -24,7 +24,10 @@ func Init(e *gin.Engine) { limiter.WithTrustForwardHeader(conf.Conf.RateLimit.TrustForwardHeader), } if conf.Conf.RateLimit.TrustedClientIPHeader != "" { - options = append(options, limiter.WithClientIPHeader(conf.Conf.RateLimit.TrustedClientIPHeader)) + options = append( + options, + limiter.WithClientIPHeader(conf.Conf.RateLimit.TrustedClientIPHeader), + ) } e.Use(NewLimiter(d, conf.Conf.RateLimit.Limit, options...)) } diff --git a/server/middlewares/log.go b/server/middlewares/log.go index 299ffcc8..2d16e517 100644 --- a/server/middlewares/log.go +++ b/server/middlewares/log.go @@ -7,18 +7,27 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/synctv-org/synctv/server/model" ) var fieldsPool = sync.Pool{ - New: func() interface{} { + New: func() any { return make(logrus.Fields, 6) }, } func NewLog(l *logrus.Logger) gin.HandlerFunc { return func(c *gin.Context) { - fields := fieldsPool.Get().(logrus.Fields) + fields, ok := fieldsPool.Get().(logrus.Fields) + if !ok { + c.JSON( + http.StatusInternalServerError, + model.NewAPIErrorResp(errors.New("invalid fields type")), + ) + return + } defer func() { clear(fields) fieldsPool.Put(fields) @@ -95,11 +104,19 @@ func formatter(param gin.LogFormatterParams) string { func GetLogger(c *gin.Context) *logrus.Entry { if log, ok := c.Get("log"); ok { - return log.(*logrus.Entry) + entry, ok := log.(*logrus.Entry) + if !ok { + panic("invalid log type") + } + return entry + } + fields, ok := fieldsPool.Get().(logrus.Fields) + if !ok { + panic("invalid fields type") } entry := &logrus.Entry{ Logger: logrus.StandardLogger(), - Data: fieldsPool.Get().(logrus.Fields), + Data: fields, } c.Set("log", entry) return entry diff --git a/server/model/admin.go b/server/model/admin.go index 5285d6bb..17f79b27 100644 --- a/server/model/admin.go +++ b/server/model/admin.go @@ -30,19 +30,21 @@ type AddUserReq struct { } func (aur *AddUserReq) Validate() error { - if aur.Username == "" { + switch { + case aur.Username == "": return errors.New("username is empty") - } else if len(aur.Username) > 32 { + case len(aur.Username) > 32: return ErrUsernameTooLong - } else if !alnumPrintHanReg.MatchString(aur.Username) { + case !alnumPrintHanReg.MatchString(aur.Username): return ErrUsernameHasInvalidChar } - if aur.Password == "" { + switch { + case aur.Password == "": return FormatEmptyPasswordError("user") - } else if len(aur.Password) > 32 { + case len(aur.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(aur.Password) { + case !alnumPrintReg.MatchString(aur.Password): return ErrPasswordHasInvalidChar } @@ -63,11 +65,12 @@ func (aur *AdminUserPasswordReq) Validate() error { return ErrInvalidID } - if aur.Password == "" { + switch { + case aur.Password == "": return FormatEmptyPasswordError("user") - } else if len(aur.Password) > 32 { + case len(aur.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(aur.Password) { + case !alnumPrintReg.MatchString(aur.Password): return ErrPasswordHasInvalidChar } @@ -88,11 +91,12 @@ func (aur *AdminUsernameReq) Validate() error { return ErrInvalidID } - if aur.Username == "" { + switch { + case aur.Username == "": return errors.New("username is empty") - } else if len(aur.Username) > 32 { + case len(aur.Username) > 32: return ErrUsernameTooLong - } else if !alnumPrintHanReg.MatchString(aur.Username) { + case !alnumPrintHanReg.MatchString(aur.Username): return ErrUsernameHasInvalidChar } @@ -113,11 +117,12 @@ func (aur *AdminRoomPasswordReq) Validate() error { return ErrInvalidID } - if aur.Password == "" { + switch { + case aur.Password == "": return FormatEmptyPasswordError("room") - } else if len(aur.Password) > 32 { + case len(aur.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(aur.Password) { + case !alnumPrintReg.MatchString(aur.Password): return ErrPasswordHasInvalidChar } diff --git a/server/model/room.go b/server/model/room.go index 4468baf2..5a5653dd 100644 --- a/server/model/room.go +++ b/server/model/room.go @@ -3,9 +3,8 @@ package model import ( "errors" - json "github.com/json-iterator/go" - "github.com/gin-gonic/gin" + json "github.com/json-iterator/go" dbModel "github.com/synctv-org/synctv/internal/model" ) @@ -37,18 +36,20 @@ func (c *CreateRoomReq) Decode(ctx *gin.Context) error { } func (c *CreateRoomReq) Validate() error { - if c.RoomName == "" { + switch { + case c.RoomName == "": return ErrEmptyRoomName - } else if len(c.RoomName) > 32 { + case len(c.RoomName) > 32: return ErrRoomNameTooLong - } else if !alnumPrintHanReg.MatchString(c.RoomName) { + case !alnumPrintHanReg.MatchString(c.RoomName): return ErrRoomNameHasInvalidChar } if c.Password != "" { - if len(c.Password) > 32 { + switch { + case len(c.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(c.Password) { + case !alnumPrintReg.MatchString(c.Password): return ErrPasswordHasInvalidChar } } diff --git a/server/model/user.go b/server/model/user.go index f7b9194a..3056cefa 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -25,11 +25,12 @@ func (s *SetUserPasswordReq) Decode(ctx *gin.Context) error { } func (s *SetUserPasswordReq) Validate() error { - if s.Password == "" { + switch { + case s.Password == "": return FormatEmptyPasswordError("user") - } else if len(s.Password) > 32 { + case len(s.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(s.Password) { + case !alnumPrintReg.MatchString(s.Password): return ErrPasswordHasInvalidChar } return nil @@ -69,11 +70,12 @@ func (l *LoginUserReq) Validate() error { } } - if l.Password == "" { + switch { + case l.Password == "": return FormatEmptyPasswordError("user") - } else if len(l.Password) > 32 { + case len(l.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(l.Password) { + case !alnumPrintReg.MatchString(l.Password): return ErrPasswordHasInvalidChar } return nil @@ -98,13 +100,12 @@ func (u *UserSignupPasswordReq) Validate() error { if !alnumPrintHanReg.MatchString(u.Username) { return ErrUsernameHasInvalidChar } - if u.Password == "" { + switch { + case u.Password == "": return FormatEmptyPasswordError("user") - } - if len(u.Password) > 32 { + case len(u.Password) > 32: return ErrPasswordTooLong - } - if !alnumPrintReg.MatchString(u.Password) { + case !alnumPrintReg.MatchString(u.Password): return ErrPasswordHasInvalidChar } return nil @@ -123,11 +124,12 @@ type SetUsernameReq struct { } func (s *SetUsernameReq) Validate() error { - if s.Username == "" { + switch { + case s.Username == "": return errors.New("username is empty") - } else if len(s.Username) > 32 { + case len(s.Username) > 32: return ErrUsernameTooLong - } else if !alnumPrintHanReg.MatchString(s.Username) { + case !alnumPrintHanReg.MatchString(s.Username): return ErrUsernameHasInvalidChar } return nil @@ -178,11 +180,12 @@ var ( ) func (u *UserSendBindEmailCaptchaReq) Validate() error { - if u.Email == "" { + switch { + case u.Email == "": return errors.New("email is empty") - } else if len(u.Email) > 128 { + case len(u.Email) > 128: return ErrEmailTooLong - } else if !emailReg.MatchString(u.Email) { + case !emailReg.MatchString(u.Email): return ErrInvalidEmail } if u.CaptchaID == "" { @@ -204,11 +207,12 @@ func (u *UserBindEmailReq) Decode(ctx *gin.Context) error { } func (u *UserBindEmailReq) Validate() error { - if u.Email == "" { + switch { + case u.Email == "": return errors.New("email is empty") - } else if len(u.Email) > 128 { + case len(u.Email) > 128: return ErrEmailTooLong - } else if !emailReg.MatchString(u.Email) { + case !emailReg.MatchString(u.Email): return ErrInvalidEmail } if u.Captcha == "" { @@ -232,11 +236,12 @@ func (u *UserSignupEmailReq) Validate() error { if err := u.UserBindEmailReq.Validate(); err != nil { return err } - if u.Password == "" { + switch { + case u.Password == "": return FormatEmptyPasswordError("user") - } else if len(u.Password) > 32 { + case len(u.Password) > 32: return ErrPasswordTooLong - } else if !alnumPrintReg.MatchString(u.Password) { + case !alnumPrintReg.MatchString(u.Password): return ErrPasswordHasInvalidChar } return nil diff --git a/server/oauth2/auth.go b/server/oauth2/auth.go index 00c8a549..4eddc241 100644 --- a/server/oauth2/auth.go +++ b/server/oauth2/auth.go @@ -7,7 +7,6 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" @@ -23,7 +22,7 @@ import ( // GET // /oauth2/login/:type func OAuth2(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) pi, err := providers.GetProvider(ctx.Param("type")) if err != nil { @@ -49,7 +48,7 @@ func OAuth2(ctx *gin.Context) { // POST func OAuth2Api(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) pi, err := providers.GetProvider(ctx.Param("type")) if err != nil { @@ -80,12 +79,15 @@ func OAuth2Api(ctx *gin.Context) { // GET // /oauth2/callback/:type func OAuth2Callback(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) code := ctx.Query("code") if code == "" { log.Errorf("invalid oauth2 code") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 code")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 code"), + ) return } @@ -99,7 +101,10 @@ func OAuth2Callback(ctx *gin.Context) { meta, loaded := states.LoadAndDelete(ctx.Query("state")) if !loaded { log.Errorf("invalid oauth2 state") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 state")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 state"), + ) return } @@ -114,7 +119,7 @@ func OAuth2Callback(ctx *gin.Context) { // POST // /oauth2/callback/:type func OAuth2CallbackAPI(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) req := model.OAuth2CallbackReq{} if err := req.Decode(ctx); err != nil { @@ -132,7 +137,10 @@ func OAuth2CallbackAPI(ctx *gin.Context) { meta, loaded := states.LoadAndDelete(req.State) if !loaded { log.Errorf("invalid oauth2 state") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 state")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 state"), + ) return } @@ -146,7 +154,7 @@ func OAuth2CallbackAPI(ctx *gin.Context) { func newAuthFunc(redirect string) stateHandler { return func(ctx *gin.Context, pi provider.Interface, code string) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) ctx.Header("X-OAuth2-Type", CallbackTypeAuth) @@ -159,19 +167,28 @@ func newAuthFunc(redirect string) stateHandler { if ui.ProviderUserID == "" { log.Errorf("invalid oauth2 provider user id") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 provider user id")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 provider user id"), + ) return } if ui.Username == "" { log.Errorf("invalid oauth2 username") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 username")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 username"), + ) return } pgs, loaded := bootstrap.ProviderGroupSettings[fmt.Sprintf("%s_%s", dbModel.SettingGroupOauth2, pi.Provider())] if !loaded { log.Errorf("invalid oauth2 provider") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 provider")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 provider"), + ) return } diff --git a/server/oauth2/bind.go b/server/oauth2/bind.go index 8351b880..ef96f9fd 100644 --- a/server/oauth2/bind.go +++ b/server/oauth2/bind.go @@ -5,18 +5,19 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/internal/provider/providers" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/utils" ) func BindAPI(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + + log := middlewares.GetLogger(ctx) pi, err := providers.GetProvider(ctx.Param("type")) if err != nil { @@ -45,8 +46,8 @@ func BindAPI(ctx *gin.Context) { } func UnBindAPI(ctx *gin.Context) { - user := ctx.MustGet("user").(*op.UserEntry).Value() - log := ctx.MustGet("log").(*logrus.Entry) + user := middlewares.GetUserEntry(ctx).Value() + log := middlewares.GetLogger(ctx) pi, err := providers.GetProvider(ctx.Param("type")) if err != nil { @@ -67,7 +68,7 @@ func UnBindAPI(ctx *gin.Context) { func newBindFunc(userID, redirect string) stateHandler { return func(ctx *gin.Context, pi provider.Interface, code string) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) ctx.Header("X-OAuth2-Type", CallbackTypeBind) @@ -80,12 +81,18 @@ func newBindFunc(userID, redirect string) stateHandler { if ui.ProviderUserID == "" { log.Errorf("invalid oauth2 provider user id") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 provider user id")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 provider user id"), + ) return } if ui.Username == "" { log.Errorf("invalid oauth2 username") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("invalid oauth2 username")) + ctx.AbortWithStatusJSON( + http.StatusBadRequest, + model.NewAPIErrorStringResp("invalid oauth2 username"), + ) return } diff --git a/server/oauth2/oauth2.go b/server/oauth2/oauth2.go index dfe05111..5a54e9d1 100644 --- a/server/oauth2/oauth2.go +++ b/server/oauth2/oauth2.go @@ -4,13 +4,13 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" + "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" ) func OAuth2EnabledAPI(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) data, err := bootstrap.Oauth2EnabledCache.Get(ctx) if err != nil { @@ -25,7 +25,7 @@ func OAuth2EnabledAPI(ctx *gin.Context) { } func OAuth2SignupEnabledAPI(ctx *gin.Context) { - log := ctx.MustGet("log").(*logrus.Entry) + log := middlewares.GetLogger(ctx) oauth2SignupEnabled, err := bootstrap.Oauth2SignupEnabledCache.Get(ctx) if err != nil { diff --git a/server/static/static.go b/server/static/static.go index 31f62272..bd6cb2c0 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -68,7 +68,7 @@ func newFSHandler(fileSys fs.FS) func(ctx *gin.Context) { func newStatCachedFSHandler(fileSys fs.FS) (func(ctx *gin.Context), error) { cache := make(map[string]struct{}) - err := fs.WalkDir(fileSys, ".", func(path string, d fs.DirEntry, err error) error { + err := fs.WalkDir(fileSys, ".", func(path string, _ fs.DirEntry, _ error) error { cache[`/`+path] = struct{}{} return nil }) diff --git a/utils/crypto.go b/utils/crypto.go index 2e181e64..40347c13 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -9,43 +9,60 @@ import ( "io" ) -func Crypto(v []byte, key []byte) ([]byte, error) { +func Crypto(v, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } - ciphertext := make([]byte, aes.BlockSize+len(v)) - iv := ciphertext[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { + // Use GCM as an AEAD mode instead of CFB + aead, err := cipher.NewGCM(block) + if err != nil { return nil, err } - stream := cipher.NewCFBEncrypter(block, iv) - stream.XORKeyStream(ciphertext[aes.BlockSize:], v) + // Create a nonce for this encryption + nonce := make([]byte, aead.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + // Encrypt and authenticate the plaintext + ciphertext := aead.Seal(nonce, nonce, v, nil) return ciphertext, nil } -func Decrypto(v []byte, key []byte) ([]byte, error) { +func Decrypto(v, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } - if len(v) < aes.BlockSize { + // Use GCM as an AEAD mode instead of CFB + aead, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + // Check if the ciphertext is at least as long as the nonce + nonceSize := aead.NonceSize() + if len(v) < nonceSize { return nil, errors.New("ciphertext too short") } - iv := v[:aes.BlockSize] - v = v[aes.BlockSize:] - stream := cipher.NewCFBDecrypter(block, iv) - stream.XORKeyStream(v, v) + // Extract the nonce from the ciphertext + nonce, ciphertext := v[:nonceSize], v[nonceSize:] + + // Decrypt and verify the ciphertext + plaintext, err := aead.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } - return v, nil + return plaintext, nil } -func CryptoToBase64(v []byte, key []byte) (string, error) { +func CryptoToBase64(v, key []byte) (string, error) { ciphertext, err := Crypto(v, key) if err != nil { return "", err @@ -63,7 +80,7 @@ func DecryptoFromBase64(v string, key []byte) ([]byte, error) { func GenCryptoKey(base string) []byte { key := make([]byte, 32) - for i := 0; i < len(base); i++ { + for i := range len(base) { key[i%32] ^= base[i] } return key @@ -71,7 +88,7 @@ func GenCryptoKey(base string) []byte { func GenCryptoKeyWithBytes(base []byte) []byte { key := make([]byte, 32) - for i := 0; i < len(base); i++ { + for i := range base { key[i%32] ^= base[i] } return key diff --git a/utils/fastJSONSerializer/fastJSONSerializer.go b/utils/fastJSONSerializer/fastJSONSerializer.go index 2d41c0c1..5c328a14 100644 --- a/utils/fastJSONSerializer/fastJSONSerializer.go +++ b/utils/fastJSONSerializer/fastJSONSerializer.go @@ -7,7 +7,6 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/zijiren233/stream" - "gorm.io/gorm/schema" ) @@ -15,7 +14,12 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary type JSONSerializer struct{} -func (*JSONSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue any) (err error) { +func (*JSONSerializer) Scan( + ctx context.Context, + field *schema.Field, + dst reflect.Value, + dbValue any, +) (err error) { fieldValue := reflect.New(field.FieldType) if dbValue != nil { @@ -41,7 +45,12 @@ func (*JSONSerializer) Scan(ctx context.Context, field *schema.Field, dst reflec return } -func (*JSONSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue any) (any, error) { +func (*JSONSerializer) Value( + _ context.Context, + _ *schema.Field, + _ reflect.Value, + fieldValue any, +) (any, error) { return json.Marshal(fieldValue) } diff --git a/utils/m3u8/m3u8.go b/utils/m3u8/m3u8.go index 40d5fd2c..9d2032f6 100644 --- a/utils/m3u8/m3u8.go +++ b/utils/m3u8/m3u8.go @@ -7,7 +7,7 @@ import ( "strings" ) -func GetM3u8AllSegments(m3u8Str string, baseURL string) ([]string, error) { +func GetM3u8AllSegments(m3u8Str, baseURL string) ([]string, error) { var segments []string err := RangeM3u8SegmentsWithBaseURL(m3u8Str, baseURL, func(segmentUrl string) (bool, error) { segments = append(segments, segmentUrl) @@ -37,7 +37,10 @@ func RangeM3u8Segments(m3u8Str string, callback func(segmentUrl string) (bool, e return nil } -func RangeM3u8SegmentsWithBaseURL(m3u8Str string, baseURL string, callback func(segmentURL string) (bool, error)) error { +func RangeM3u8SegmentsWithBaseURL( + m3u8Str, baseURL string, + callback func(segmentURL string) (bool, error), +) error { baseURLParsed, err := url.Parse(baseURL) if err != nil { return fmt.Errorf("parse base url error: %w", err) @@ -54,7 +57,10 @@ func RangeM3u8SegmentsWithBaseURL(m3u8Str string, baseURL string, callback func( }) } -func ReplaceM3u8Segments(m3u8Str string, callback func(segmentURL string) (string, error)) (string, error) { +func ReplaceM3u8Segments( + m3u8Str string, + callback func(segmentURL string) (string, error), +) (string, error) { var result strings.Builder scanner := bufio.NewScanner(strings.NewReader(m3u8Str)) for scanner.Scan() { @@ -76,7 +82,10 @@ func ReplaceM3u8Segments(m3u8Str string, callback func(segmentURL string) (strin return result.String(), nil } -func ReplaceM3u8SegmentsWithBaseURL(m3u8Str string, baseURL string, callback func(segmentURL string) (string, error)) (string, error) { +func ReplaceM3u8SegmentsWithBaseURL( + m3u8Str, baseURL string, + callback func(segmentURL string) (string, error), +) (string, error) { baseURLParsed, err := url.Parse(baseURL) if err != nil { return "", fmt.Errorf("parse base url error: %w", err) diff --git a/utils/smtp/format.go b/utils/smtp/format.go index 7c130157..6ac28bf8 100644 --- a/utils/smtp/format.go +++ b/utils/smtp/format.go @@ -45,7 +45,7 @@ func WithContentTransferEncoding(contentTransferEncoding string) FormatMailOptio } } -func FormatMail(from string, to []string, subject string, body string, opts ...FormatMailOption) string { +func FormatMail(from string, to []string, subject, body string, opts ...FormatMailOption) string { c := &FormatMailConfig{ date: time.Now().Format(time.RFC1123Z), mimeVersion: "1.0", @@ -57,14 +57,14 @@ func FormatMail(from string, to []string, subject string, body string, opts ...F } buf := bytes.NewBuffer(nil) - buf.WriteString(fmt.Sprintf("From: %s\r\n", from)) - buf.WriteString(fmt.Sprintf("To: %s\r\n", strings.Join(to, ", "))) - buf.WriteString(fmt.Sprintf("Subject: %s\r\n", mime.QEncoding.Encode("UTF-8", subject))) - buf.WriteString(fmt.Sprintf("Date: %s\r\n", c.date)) - buf.WriteString(fmt.Sprintf("MIME-Version: %s\r\n", c.mimeVersion)) - buf.WriteString(fmt.Sprintf("Content-Type: %s\r\n", c.contentType)) + fmt.Fprintf(buf, "From: %s\r\n", from) + fmt.Fprintf(buf, "To: %s\r\n", strings.Join(to, ", ")) + fmt.Fprintf(buf, "Subject: %s\r\n", mime.QEncoding.Encode("UTF-8", subject)) + fmt.Fprintf(buf, "Date: %s\r\n", c.date) + fmt.Fprintf(buf, "MIME-Version: %s\r\n", c.mimeVersion) + fmt.Fprintf(buf, "Content-Type: %s\r\n", c.contentType) if c.contentTransferEncoding != "" { - buf.WriteString(fmt.Sprintf("Content-Transfer-Encoding: %s\r\n", c.contentTransferEncoding)) + fmt.Fprintf(buf, "Content-Transfer-Encoding: %s\r\n", c.contentTransferEncoding) } buf.WriteString("\r\n") @@ -86,7 +86,13 @@ func FormatMail(from string, to []string, subject string, body string, opts ...F return buf.String() } -func SendEmail(cli *smtp.Client, from string, to []string, subject, body string, opts ...FormatMailOption) error { +func SendEmail( + cli *smtp.Client, + from string, + to []string, + subject, body string, + opts ...FormatMailOption, +) error { return cli.SendMail( from, to, diff --git a/utils/utils.go b/utils/utils.go index 99defa45..5f2301c6 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -32,7 +32,7 @@ func init() { var ( letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") noRedirectHTTPClient = &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { + CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }, } @@ -153,7 +153,7 @@ func CompVersion(v1, v2 string) (int, error) { } // Compare base version numbers - for i := 0; i < len(v1Base); i++ { + for i := range v1Base { if v1Base[i] > v2Base[i] { return VersionGreater, nil } @@ -235,20 +235,22 @@ type Once struct { func (o *Once) Done() (doned bool) { done := atomic.LoadUint32(&o.done) - if done == 1 { + switch done { + case 1: return true - } else if done == 2 { + case 2: return false } o.m.Lock() defer o.m.Unlock() - if o.done == 0 { + switch o.done { + case 0: doned = false atomic.StoreUint32(&o.done, 2) - } else if o.done == 1 { + case 1: doned = true - } else { + default: doned = false } return @@ -402,7 +404,7 @@ func ForceColor() bool { return needColor } -func GetPageAndMax(ctx *gin.Context) (page int, _max int, err error) { +func GetPageAndMax(ctx *gin.Context) (page, _max int, err error) { _max, err = strconv.Atoi(ctx.DefaultQuery("max", "10")) if err != nil { return 0, 0, errors.New("max must be a number") diff --git a/utils/utils_test.go b/utils/utils_test.go index 6ad522c7..3a2d58c1 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -49,7 +49,10 @@ func TestGetPageItems(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := utils.GetPageItems(tt.args.items, tt.args.page, tt.args.pageSize); !reflect.DeepEqual(got, tt.want) { + if got := utils.GetPageItems(tt.args.items, tt.args.page, tt.args.pageSize); !reflect.DeepEqual( + got, + tt.want, + ) { t.Errorf("GetPageItems() = %v, want %v", got, tt.want) } }) diff --git a/utils/websocket.go b/utils/websocket.go index 53d1b074..934a132e 100644 --- a/utils/websocket.go +++ b/utils/websocket.go @@ -31,7 +31,12 @@ func NewWebSocketServer(conf ...WebSocketConfig) *WebSocket { return ws } -func (ws *WebSocket) Server(w http.ResponseWriter, r *http.Request, subprotocols []string, handler func(c *websocket.Conn) error) error { +func (ws *WebSocket) Server( + w http.ResponseWriter, + r *http.Request, + subprotocols []string, + handler func(c *websocket.Conn) error, +) error { conf := []UpgraderConf{} if len(subprotocols) > 0 { conf = append(conf, WithSubprotocols(subprotocols)) @@ -57,7 +62,7 @@ func (ws *WebSocket) newUpgrader(conf ...UpgraderConf) *websocket.Upgrader { HandshakeTimeout: time.Second * 30, ReadBufferSize: 1024, WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { + CheckOrigin: func(_ *http.Request) bool { return true }, } @@ -67,7 +72,12 @@ func (ws *WebSocket) newUpgrader(conf ...UpgraderConf) *websocket.Upgrader { return ug } -func (ws *WebSocket) NewWebSocketClient(w http.ResponseWriter, r *http.Request, responseHeader http.Header, conf ...UpgraderConf) (*websocket.Conn, error) { +func (ws *WebSocket) NewWebSocketClient( + w http.ResponseWriter, + r *http.Request, + responseHeader http.Header, + conf ...UpgraderConf, +) (*websocket.Conn, error) { conn, err := ws.newUpgrader(conf...).Upgrade(w, r, responseHeader) if err != nil { return nil, err