pull/322/head
zijiren233 1 year ago
parent 0df11b9592
commit 43ba071e35

@ -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

@ -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

@ -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

@ -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

@ -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
},
}

@ -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
},
}

@ -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
},

@ -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() {

@ -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
},
}

@ -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
},
}

@ -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
},

@ -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)

@ -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")
}

@ -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
},
}

@ -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 {

@ -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
},
}

@ -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
},
}

@ -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
},

@ -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
},
}

@ -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)

@ -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

209
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=

@ -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")

@ -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 {

@ -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 {

@ -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
}
}

@ -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())

@ -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)
}

@ -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")
}

@ -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

@ -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()
}

@ -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
}

@ -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

@ -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)
}

@ -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

@ -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...)
}

@ -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

@ -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()
}
}

@ -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,
)
}

@ -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, <name> postgres: disable, require, verify-ca, verify-full" yaml:"ssl_mode"`
SslMode string `env:"DATABASE_SSL_MODE" hc:"mysql: true, false, skip-verify, preferred, <name> 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 {

@ -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"`

@ -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 {

@ -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,
},

@ -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:

@ -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)
}

@ -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)
})
}

@ -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)
}

@ -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),

@ -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)
}

@ -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 {

@ -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)
}

@ -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
}

@ -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()

@ -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
}

@ -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"`

@ -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)"`

@ -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)

@ -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 {

@ -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

@ -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

@ -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:
}
}

@ -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
}

@ -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")

@ -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
}

@ -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

@ -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 {

@ -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

@ -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
}

@ -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)
}

@ -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()
}

@ -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

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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)
}

@ -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
}

@ -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
}

@ -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",

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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",

@ -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
}

@ -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
}

@ -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
}

@ -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) {

@ -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
}

@ -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

@ -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

@ -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 {

@ -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

@ -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",

@ -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 {

@ -6,7 +6,6 @@ import (
"sync"
log "github.com/sirupsen/logrus"
"github.com/zijiren233/gencontainer/pqueue"
"github.com/zijiren233/gencontainer/rwmap"
)

@ -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 {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save