diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 34fc9495..f377a3a0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -55,8 +55,8 @@ jobs: uses: docker/metadata-action@v4 with: images: | - justsong/one-api - ghcr.io/${{ github.repository }} + ${{ contains(github.ref, 'alpha') && 'justsong/one-api-alpha' || 'justsong/one-api' }} + ${{ contains(github.ref, 'alpha') && format('ghcr.io/{0}-alpha', github.repository) || format('ghcr.io/{0}', github.repository) }} - name: Build and push Docker images uses: docker/build-push-action@v3 diff --git a/.gitignore b/.gitignore index e7f1e0a2..b4be99a7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ node_modules cmd.md .env /one-api +temp +.DS_Store \ No newline at end of file diff --git a/README.en.md b/README.en.md index c9fdbbc8..2741062e 100644 --- a/README.en.md +++ b/README.en.md @@ -52,8 +52,6 @@ _✨ Access all LLM through the standard OpenAI API format, easy to deploy & use > **Warning**: This README is translated by ChatGPT. Please feel free to submit a PR if you find any translation errors. -> **Warning**: The Docker image for English version is `justsong/one-api-en`. - > **Note**: The latest image pulled from Docker may be an `alpha` release. Specify the version manually if you require stability. ## Features @@ -89,7 +87,9 @@ _✨ Access all LLM through the standard OpenAI API format, easy to deploy & use ## Deployment ### Docker Deployment -Deployment command: `docker run --name one-api -d --restart always -p 3000:3000 -e TZ=Asia/Shanghai -v /home/ubuntu/data/one-api:/data justsong/one-api-en` + +Deployment command: +`docker run --name one-api -d --restart always -p 3000:3000 -e TZ=Asia/Shanghai -v /home/ubuntu/data/one-api:/data justsong/one-api` Update command: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR` diff --git a/README.ja.md b/README.ja.md index c15915ec..4642df7d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -52,8 +52,6 @@ _✨ 標準的な OpenAI API フォーマットを通じてすべての LLM に > **警告**: この README は ChatGPT によって翻訳されています。翻訳ミスを発見した場合は遠慮なく PR を投稿してください。 -> **警告**: 英語版の Docker イメージは `justsong/one-api-en` です。 - > **注**: Docker からプルされた最新のイメージは、`alpha` リリースかもしれません。安定性が必要な場合は、手動でバージョンを指定してください。 ## 特徴 @@ -89,7 +87,9 @@ _✨ 標準的な OpenAI API フォーマットを通じてすべての LLM に ## デプロイメント ### Docker デプロイメント -デプロイコマンド: `docker run --name one-api -d --restart always -p 3000:3000 -e TZ=Asia/Shanghai -v /home/ubuntu/data/one-api:/data justsong/one-api-en`。 + +デプロイコマンド: +`docker run --name one-api -d --restart always -p 3000:3000 -e TZ=Asia/Shanghai -v /home/ubuntu/data/one-api:/data justsong/one-api`。 コマンドを更新する: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrr/watchtower -cR`。 diff --git a/common/config/config.go b/common/config/config.go index 3114ee1b..1d28975b 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -140,10 +140,10 @@ var ValidThemes = map[string]bool{ // All duration's unit is seconds // Shouldn't larger then RateLimitKeyExpirationDuration var ( - GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 240) + GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 480) GlobalApiRateLimitDuration int64 = 3 * 60 - GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 120) + GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 240) GlobalWebRateLimitDuration int64 = 3 * 60 UploadRateLimitNum = 10 diff --git a/common/i18n/i18n.go b/common/i18n/i18n.go new file mode 100644 index 00000000..dfad6eac --- /dev/null +++ b/common/i18n/i18n.go @@ -0,0 +1,72 @@ +package i18n + +import ( + "embed" + "encoding/json" + "strings" + + "github.com/gin-gonic/gin" +) + +//go:embed locales/*.json +var localesFS embed.FS + +var ( + translations = make(map[string]map[string]string) + defaultLang = "en" + ContextKey = "i18n" +) + +// Init loads all translation files from embedded filesystem +func Init() error { + entries, err := localesFS.ReadDir("locales") + if err != nil { + return err + } + + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") { + continue + } + + langCode := strings.TrimSuffix(entry.Name(), ".json") + content, err := localesFS.ReadFile("locales/" + entry.Name()) + if err != nil { + return err + } + + var translation map[string]string + if err := json.Unmarshal(content, &translation); err != nil { + return err + } + translations[langCode] = translation + } + + return nil +} + +func GetLang(c *gin.Context) string { + rawLang, ok := c.Get(ContextKey) + if !ok { + return defaultLang + } + lang, _ := rawLang.(string) + if lang != "" { + return lang + } + return defaultLang +} + +func Translate(c *gin.Context, message string) string { + lang := GetLang(c) + return translateHelper(lang, message) +} + +func translateHelper(lang, message string) string { + if trans, ok := translations[lang]; ok { + if translated, exists := trans[message]; exists { + return translated + } + } + return message +} diff --git a/common/i18n/locales/en.json b/common/i18n/locales/en.json new file mode 100644 index 00000000..4b24dea7 --- /dev/null +++ b/common/i18n/locales/en.json @@ -0,0 +1,5 @@ +{ + "invalid_input": "Invalid input, please check your input", + "send_email_failed": "failed to send email: ", + "invalid_parameter": "invalid parameter" +} diff --git a/common/i18n/locales/zh-CN.json b/common/i18n/locales/zh-CN.json new file mode 100644 index 00000000..805d5c5a --- /dev/null +++ b/common/i18n/locales/zh-CN.json @@ -0,0 +1,5 @@ +{ + "invalid_input": "无效的输入,请检查您的输入", + "send_email_failed": "发送邮件失败:", + "invalid_parameter": "无效的参数" +} diff --git a/common/logger/logger.go b/common/logger/logger.go index 1e3bc254..b5348033 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -57,6 +57,14 @@ func SysLogf(format string, a ...any) { logHelper(nil, loggerINFO, fmt.Sprintf(format, a...)) } +func SysWarn(s string) { + logHelper(nil, loggerWarn, s) +} + +func SysWarnf(format string, a ...any) { + logHelper(nil, loggerWarn, fmt.Sprintf(format, a...)) +} + func SysError(s string) { logHelper(nil, loggerError, s) } diff --git a/common/message/email.go b/common/message/email.go index 6ba27019..0a047b49 100644 --- a/common/message/email.go +++ b/common/message/email.go @@ -5,12 +5,14 @@ import ( "crypto/tls" "encoding/base64" "fmt" - "github.com/pkg/errors" - "github.com/songquanpeng/one-api/common/config" "net" "net/smtp" "strings" "time" + + "github.com/pkg/errors" + "github.com/songquanpeng/one-api/common/config" + "github.com/songquanpeng/one-api/common/logger" ) func shouldAuth() bool { @@ -99,8 +101,12 @@ func SendEmail(subject string, receiver string, content string) error { if err != nil { return err } - } else { - err = smtp.SendMail(addr, auth, config.SMTPAccount, to, mail) + return nil + } + err = smtp.SendMail(addr, auth, config.SMTPAccount, to, mail) + if err != nil && strings.Contains(err.Error(), "short response") { // 部分提供商返回该错误,但实际上邮件已经发送成功 + logger.SysWarnf("short response from SMTP server, return nil instead of error: %s", err.Error()) + return nil } return err } diff --git a/common/message/template.go b/common/message/template.go new file mode 100644 index 00000000..55733725 --- /dev/null +++ b/common/message/template.go @@ -0,0 +1,34 @@ +package message + +import ( + "fmt" + + "github.com/songquanpeng/one-api/common/config" +) + +// EmailTemplate 生成美观的 HTML 邮件内容 +func EmailTemplate(title, content string) string { + return fmt.Sprintf(` + + +
+ + + + +此邮件由系统自动发送,请勿直接回复
+%s
+Hello, you are verifying %s email.
"+ - "Your verification code is: %s
"+ - "The verification code is valid within %d minutes. If it is not your operation, please ignore it.
", config.SystemName, code, common.VerificationValidMinutes) + subject := fmt.Sprintf("%s 邮箱验证邮件", config.SystemName) + content := message.EmailTemplate( + subject, + fmt.Sprintf(` +Hello!
+You are verifying your email for %s.
+Your verification code is:
+%s
+The verification code is valid for %d minutes. If you did not request this, please ignore.
+ `, config.SystemName, code, common.VerificationValidMinutes), + ) err := message.SendEmail(subject, email, content) if err != nil { c.JSON(http.StatusOK, gin.H{ @@ -138,7 +146,7 @@ func SendPasswordResetEmail(c *gin.Context) { if err := common.Validate.Var(email, "required,email"); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } @@ -152,16 +160,26 @@ func SendPasswordResetEmail(c *gin.Context) { code := common.GenerateVerificationCode(0) common.RegisterVerificationCodeWithKey(email, code, common.PasswordResetPurpose) link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code) - subject := fmt.Sprintf("%s Password reset", config.SystemName) - content := fmt.Sprintf("Hello, you are resetting %s password.
"+ - "Click here to reset your password.
"+ - "If the link cannot be clicked, please try clicking the link below or copy it to your browser to open:
%s
The reset link is valid within %d minutes. If it is not your operation, please ignore it.
", config.SystemName, link, link, common.VerificationValidMinutes) + subject := fmt.Sprintf("%s 密码重置", config.SystemName) + content := message.EmailTemplate( + subject, + fmt.Sprintf(` +Hello!
+You are resetting your password for %s.
+Please click the button below to reset your password:
++ Reset Password +
+If the button doesn't work, please copy the following link and paste it into your browser:
+%s
+The reset link is valid for %d minutes. If you didn't request this, please ignore.
+ `, config.SystemName, link, link, common.VerificationValidMinutes), + ) err := message.SendEmail(subject, email, content) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": err.Error(), + "message": fmt.Sprintf("%s%s", i18n.Translate(c, "send_email_failed"), err.Error()), }) return } @@ -183,7 +201,7 @@ func ResetPassword(c *gin.Context) { if req.Email == "" || req.Token == "" { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } diff --git a/controller/option.go b/controller/option.go index 380ad1c6..742952bd 100644 --- a/controller/option.go +++ b/controller/option.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/songquanpeng/one-api/common/config" "github.com/songquanpeng/one-api/common/helper" + "github.com/songquanpeng/one-api/common/i18n" "github.com/songquanpeng/one-api/model" ) @@ -38,7 +39,7 @@ func UpdateOption(c *gin.Context) { if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } diff --git a/controller/user.go b/controller/user.go index 4a21782a..77b6c024 100644 --- a/controller/user.go +++ b/controller/user.go @@ -12,6 +12,7 @@ import ( "github.com/songquanpeng/one-api/common" "github.com/songquanpeng/one-api/common/config" "github.com/songquanpeng/one-api/common/ctxkey" + "github.com/songquanpeng/one-api/common/i18n" "github.com/songquanpeng/one-api/common/random" "github.com/songquanpeng/one-api/model" ) @@ -33,7 +34,7 @@ func Login(c *gin.Context) { err := json.NewDecoder(c.Request.Body).Decode(&loginRequest) if err != nil { c.JSON(http.StatusOK, gin.H{ - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), "success": false, }) return @@ -42,7 +43,7 @@ func Login(c *gin.Context) { password := loginRequest.Password if username == "" || password == "" { c.JSON(http.StatusOK, gin.H{ - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), "success": false, }) return @@ -135,14 +136,14 @@ func Register(c *gin.Context) { if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } if err := common.Validate.Struct(&user); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Input is illegal " + err.Error(), + "message": i18n.Translate(c, "invalid_input"), }) return } @@ -384,7 +385,7 @@ func UpdateUser(c *gin.Context) { if err != nil || updatedUser.Id == 0 { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } @@ -394,7 +395,7 @@ func UpdateUser(c *gin.Context) { if err := common.Validate.Struct(&updatedUser); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Input is illegal " + err.Error(), + "message": i18n.Translate(c, "invalid_input"), }) return } @@ -448,7 +449,7 @@ func UpdateSelf(c *gin.Context) { if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } @@ -558,14 +559,14 @@ func CreateUser(c *gin.Context) { if err != nil || user.Username == "" || user.Password == "" { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } if err := common.Validate.Struct(&user); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Input is illegal " + err.Error(), + "message": i18n.Translate(c, "invalid_input"), }) return } @@ -614,7 +615,7 @@ func ManageUser(c *gin.Context) { if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "Invalid parameter", + "message": i18n.Translate(c, "invalid_parameter"), }) return } diff --git a/go.mod b/go.mod index 6e7dbd40..c3031718 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,16 @@ toolchain go1.23.0 require ( cloud.google.com/go/iam v1.3.1 - github.com/Laisky/go-utils/v4 v4.10.0 - github.com/aws/aws-sdk-go-v2 v1.32.7 - github.com/aws/aws-sdk-go-v2/credentials v1.17.48 - github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.23.1 + github.com/Laisky/go-utils/v5 v5.0.0 + github.com/aws/aws-sdk-go-v2 v1.36.0 + github.com/aws/aws-sdk-go-v2/credentials v1.17.57 + github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3 github.com/gin-contrib/cors v1.7.3 - github.com/gin-contrib/gzip v1.1.0 + github.com/gin-contrib/gzip v1.2.2 github.com/gin-contrib/sessions v1.0.2 github.com/gin-contrib/static v1.1.3 github.com/gin-gonic/gin v1.10.0 - github.com/go-playground/validator/v10 v10.23.0 + github.com/go-playground/validator/v10 v10.24.0 github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 @@ -30,7 +30,7 @@ require ( golang.org/x/crypto v0.32.0 golang.org/x/image v0.23.0 golang.org/x/sync v0.10.0 - google.golang.org/api v0.215.0 + google.golang.org/api v0.219.0 gorm.io/driver/mysql v1.5.7 gorm.io/driver/postgres v1.5.11 gorm.io/driver/sqlite v1.5.7 @@ -38,8 +38,8 @@ require ( ) require ( - cloud.google.com/go/auth v0.13.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/auth v0.14.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect github.com/GoWebProd/gip v0.0.0-20230623090727-b60d41d5d320 // indirect github.com/GoWebProd/uuid7 v0.0.0-20231130161441-17ee54b097d4 // indirect @@ -49,24 +49,23 @@ require ( github.com/Laisky/golang-fifo v1.0.1-0.20240403091456-fc83d5e38c0b // indirect github.com/Laisky/graphql v1.0.6 // indirect github.com/Laisky/zap v1.27.1-0.20240628060440-a253d90172e3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect - github.com/aws/smithy-go v1.22.1 // indirect - github.com/bytedance/sonic v1.12.6 // indirect - github.com/bytedance/sonic/loader v0.2.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect + github.com/aws/smithy-go v1.22.2 // indirect + github.com/bytedance/sonic v1.12.7 // indirect + github.com/bytedance/sonic/loader v0.2.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gammazero/deque v0.2.1 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-contrib/sse v1.0.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -74,7 +73,7 @@ require ( github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 // indirect - github.com/google/s2a-go v0.1.8 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect @@ -92,34 +91,34 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/leodido/go-urn v1.4.0 // 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.22 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/smarty/assertions v1.15.0 // indirect - github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect + github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/arch v0.12.0 // indirect + golang.org/x/arch v0.13.0 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.8.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect - google.golang.org/grpc v1.67.3 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 57064d15..350f1af8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= -cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= +cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= 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/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= @@ -16,8 +16,8 @@ github.com/Laisky/fast-skiplist/v2 v2.0.1 h1:mZD3G/cwNovXsd21Vyvt3HCI9dEg1V7OD64 github.com/Laisky/fast-skiplist/v2 v2.0.1/go.mod h1:JlDGOmsJOwW7Uo46L9aVG7nJAeqP7X7nfU5TABOiiE8= github.com/Laisky/go-chaining v0.0.0-20180507092046-43dcdc5a21be h1:7Rxhm6IjOtDAyj8eScOFntevwzkWhx94zi48lxo4m4w= github.com/Laisky/go-chaining v0.0.0-20180507092046-43dcdc5a21be/go.mod h1:1mdzaETo0kjvCQICPSePsoaatJN4l7JvEA1200lyevo= -github.com/Laisky/go-utils/v4 v4.10.0 h1:kSYHk0ONde1ZVMNVw/sLAOAKu/+9mOS9KtVLtY0upAo= -github.com/Laisky/go-utils/v4 v4.10.0/go.mod h1:TepxY90+WGujsezm9rN7Ctk8faB2Xgtz4bQ/C8PHBJ8= +github.com/Laisky/go-utils/v5 v5.0.0 h1:Cn7Xy3nnWI9bOZpiZ+msRM9953AneXDTqaFAwIzXM2Y= +github.com/Laisky/go-utils/v5 v5.0.0/go.mod h1:4v7RGRQvvECSiuND4d1N3sW09A+GcclZUHQnbAkX+T0= github.com/Laisky/golang-fifo v1.0.1-0.20240403091456-fc83d5e38c0b h1:o2BuVyXFkDTkEiuz1Ur32jGvaErgEHqhb8AtTIkrvE0= github.com/Laisky/golang-fifo v1.0.1-0.20240403091456-fc83d5e38c0b/go.mod h1:j90tUqwBaEncIzpAd6ZGPZHWjclgAyMY8fdOqsewitE= github.com/Laisky/graphql v1.0.6 h1:NEULGxfxo+wbsW2OmqBXOMNUGgqo8uFjWNabwuNK10g= @@ -26,34 +26,33 @@ github.com/Laisky/zap v1.27.1-0.20240628060440-a253d90172e3 h1:SD0siYXoInGc6MqVs github.com/Laisky/zap v1.27.1-0.20240628060440-a253d90172e3/go.mod h1:HABqM5YDQlPq8w+Pmp9h/x9F6Vy+3oHBLP+2+pBoaJw= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= -github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= -github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.23.1 h1:rqrvjFScEwD7VfP4L0hhnrXyTkgUkpQWAdwOrW2slOo= -github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.23.1/go.mod h1:Vn5GopXsOAC6kbwzjfM6V37dxc4mo4J4xCRiF27pSZA= -github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= -github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk= +github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 h1:zAxi9p3wsZMIaVCdoiQp2uZ9k1LsZvmAnoTBeZPXom0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8/go.mod h1:3XkePX5dSaxveLAYY7nsbsZZrKxCyEuE5pM4ziFxyGg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3 h1:GXQrb3kyg4EU94onCRH/oG2IsVjHMNE+IPE4RGkgSa4= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3/go.mod h1:PKGlRhLmSZuA6iCbRD1oZKrTJHdm6NWwWBvHxfDNHTA= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8= github.com/brianvoe/gofakeit/v6 v6.23.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk= -github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q= +github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= -github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.2 h1:jxAJuN9fOot/cyz5Q6dUuMJF5OqQ6+5GfA8FjjQ0R4o= +github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -68,18 +67,18 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= -github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +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/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= 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/gzip v1.1.0 h1:kVw7Nr9M+Z6Ch4qo7aGMbiqxDeyQFru+07MgAcUF62M= -github.com/gin-contrib/gzip v1.1.0/go.mod h1:iHJXCup4CWiKyPUEl+GwkHjchl+YyYuMKbOCiXujPIA= +github.com/gin-contrib/gzip v1.2.2 h1:iUU/EYCM8ENfkjmZaVrxbjF/ZC267Iqv5S0MMCMEliI= +github.com/gin-contrib/gzip v1.2.2/go.mod h1:C1a5cacjlDsS20cKnHlZRCPUu57D3qH6B2pV0rl+Y/s= github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA= github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +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-contrib/static v1.1.3 h1:WLOpkBtMDJ3gATFZgNJyVibFMio/UHonnueqJsQ0w4U= github.com/gin-contrib/static v1.1.3/go.mod h1:zejpJ/YWp8cZj/6EpiL5f/+skv5daQTNwRx1E8Pci30= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= @@ -98,8 +97,8 @@ 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.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -108,6 +107,8 @@ github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -116,8 +117,8 @@ github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932/go.mod h1:cC6EdPbj/1 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= 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= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= @@ -167,8 +168,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 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.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -204,15 +205,17 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/stretchr/objx v0.1.0/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= -github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b h1:MNaGusDfB1qxEsl6iVb33Gbe777IKzPP5PDta0xGC8M= +github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= 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= @@ -222,21 +225,25 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= -golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA= +golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= @@ -250,10 +257,10 @@ golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -267,23 +274,23 @@ golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= -google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0= -google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY= +google.golang.org/api v0.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0= +google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= -google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8= -google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/i18n/en.json b/i18n/en.json deleted file mode 100644 index b7f1bd3e..00000000 --- a/i18n/en.json +++ /dev/null @@ -1,778 +0,0 @@ -{ - "$%.6f 额度": "$%.6f quota", - "%d 点额度": "%d point quota", - "尚未实现": "Not yet implemented", - "余额不足": "Insufficient balance", - "危险操作": "Hazardous operations", - "输入你的账户名": "Enter your account name", - "确认删除": "Confirm Delete", - "确认绑定": "Confirm Binding", - "您正在删除自己的帐户,将清空所有数据且不可恢复": "You are deleting your account, all data will be cleared and unrecoverable.", - "\"渠道「%s」(#%d)已被禁用\"": "\"Channel %s (#%d) has been disabled\"", - "渠道「%s」(#%d)已被禁用,原因:%s": "Channel %s (#%d) has been disabled, reason: %s", - "测试已在运行中": "Test is already running", - "响应时间 %.2fs 超过阈值 %.2fs": "Response time %.2fs exceeds threshold %.2fs", - "渠道测试完成": "Channel test completed", - "渠道测试完成,如果没有收到禁用通知,说明所有渠道都正常": "Channel test completed, if you have not received the disable notification, it means that all channels are normal", - "无法连接至 GitHub 服务器,请稍后重试!": "Unable to connect to GitHub server, please try again later!", - "返回值非法,用户字段为空,请稍后重试!": "The return value is illegal, the user field is empty, please try again later!", - "管理员未开启通过 GitHub 登录以及注册": "The administrator did not turn on login and registration via GitHub", - "管理员关闭了新用户注册": "The administrator has turned off new user registration", - "用户已被封禁": "User has been banned", - "该 GitHub 账户已被绑定": "The GitHub account has been bound", - "邮箱地址已被占用": "Email address is occupied", - "%s邮箱验证邮件": "%s Email verification email", - "您好,你正在进行%s邮箱验证。
": "Hello, you are verifying %s email.
", - "您的验证码为: %s
": "Your verification code is: %s
", - "验证码 %d 分钟内有效,如果不是本人操作,请忽略。
": "The verification code is valid within %d minutes. If it is not your operation, please ignore it.
", - "无效的参数": "Invalid parameter", - "该邮箱地址未注册": "The email address is not registered", - "%s密码重置": "%s Password reset", - "您好,你正在进行%s密码重置。
": "Hello, you are resetting %s password.
", - "点击此处进行密码重置。
": "Click here to reset your password.
", - "重置链接 %d 分钟内有效,如果不是本人操作,请忽略。
": "The reset link is valid within %d minutes. If it is not your operation, please ignore it.
", - "重置链接非法或已过期": "Reset link is illegal or expired", - "无法启用 GitHub OAuth,请先填入 GitHub Client ID 以及 GitHub Client Secret!": "Unable to enable GitHub OAuth, please fill in GitHub Client ID and GitHub Client Secret first!", - "无法启用微信登录,请先填入微信登录相关配置信息!": "Unable to enable WeChat login, please fill in the relevant configuration information for WeChat login first!", - "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!": "Unable to enable Turnstile verification, please fill in the relevant configuration information for Turnstile verification first!", - "兑换码名称长度必须在1-20之间": "The length of the redemption code name must be between 1-20", - "兑换码个数必须大于0": "The number of redemption codes must be greater than 0", - "一次兑换码批量生成的个数不能大于 100": "The number of redemption codes generated in a batch cannot be greater than 100", - "通过令牌「%s」使用模型 %s 消耗 %s(模型倍率 %.2f,分组倍率 %.2f)": "Using model %s with token %s consumes %s (model rate %.2f, group rate %.2f)", - "当前分组上游负载已饱和,请稍后再试": "The current group load is saturated, please try again later", - "令牌名称过长": "Token name is too long", - "令牌已过期,无法启用,请先修改令牌过期时间,或者设置为永不过期": "The token has expired and cannot be enabled. Please modify the expiration time of the token, or set it to never expire.", - "令牌可用额度已用尽,无法启用,请先修改令牌剩余额度,或者设置为无限额度": "The available quota of the token has been used up and cannot be enabled. Please modify the remaining quota of the token, or set it to unlimited quota", - "管理员关闭了密码登录": "The administrator has turned off password login", - "无法保存会话信息,请重试": "Unable to save session information, please try again", - "管理员关闭了通过密码进行注册,请使用第三方账户验证的形式进行注册": "The administrator has turned off registration via password. Please use the form of third-party account verification to register", - "输入不合法 ": "Input is illegal ", - "管理员开启了邮箱验证,请输入邮箱地址和验证码": "The administrator has turned on email verification, please enter the email address and verification code", - "验证码错误或已过期": "Verification code error or expired", - "无权获取同级或更高等级用户的信息": "No permission to get information of users at the same level or higher", - "请重试,系统生成的 UUID 竟然重复了!": "Please try again, the system-generated UUID is actually duplicated!", - "输入不合法": "Input is illegal", - "无权更新同权限等级或更高权限等级的用户信息": "No permission to update user information with the same permission level or higher permission level", - "管理员将用户额度从 %s修改为 %s": "The administrator changed the user quota from %s to %s", - "无权删除同权限等级或更高权限等级的用户": "No permission to delete users with the same permission level or higher permission level", - "无法创建权限大于等于自己的用户": "Unable to create users with permissions greater than or equal to your own", - "用户不存在": "User does not exist", - "无法禁用超级管理员用户": "Unable to disable super administrator user", - "无法删除超级管理员用户": "Unable to delete super administrator user", - "普通管理员用户无法提升其他用户为管理员": "Ordinary administrator users cannot promote other users to administrators", - "该用户已经是管理员": "The user is already an administrator", - "无法降级超级管理员用户": "Unable to downgrade super administrator user", - "该用户已经是普通用户": "The user is already an ordinary user", - "管理员未开启通过微信登录以及注册": "The administrator has not enabled login and registration via WeChat", - "该微信账号已被绑定": "The WeChat account has been bound", - "无权进行此操作,未登录且未提供 access token": "No permission to perform this operation, not logged in and no access token provided", - "无权进行此操作,access token 无效": "No permission to perform this operation, access token is invalid", - "无权进行此操作,权限不足": "No permission to perform this operation, insufficient permissions", - "普通用户不支持指定渠道": "Ordinary users do not support specifying channels", - "无效的渠道 ID": "Invalid channel ID", - "该渠道已被禁用": "The channel has been disabled", - "无效的请求": "Invalid request", - "无可用渠道": "No available channels", - "Turnstile token 为空": "Turnstile token is empty", - "Turnstile 校验失败,请刷新重试!": "Turnstile verification failed, please refresh and try again!", - "id 为空!": "id is empty!", - "未提供兑换码": "No redemption code provided", - "无效的 user id": "Invalid user id", - "无效的兑换码": "Invalid redemption code", - "该兑换码已被使用": "The redemption code has been used", - "通过兑换码充值 %s": "Recharge %s through redemption code", - "未提供令牌": "No token provided", - "该令牌状态不可用": "The token status is not available", - "该令牌已过期": "The token has expired", - "该令牌额度已用尽": "The token quota has been used up", - "无效的令牌": "Invalid token", - "令牌验证失败": "Token verification failed", - "id 或 userId 为空!": "id or userId is empty!", - "quota 不能为负数!": "quota cannot be negative!", - "令牌额度不足": "Insufficient token quota", - "用户额度不足": "Insufficient user quota", - "您的额度即将用尽": "Your quota is about to run out", - "您的额度已用尽": "Your quota has been used up", - "%s,当前剩余额度为 %d,为了不影响您的使用,请及时充值。Hello!
+%s, your current remaining quota is %d.
+To avoid any disruption to your service, please top up in a timely manner.
++ Top Up Now +
+If the button does not work, please copy the following link and paste it into your browser:
+%s
+ `, contentText, userQuota, topUpLink, topUpLink), + ) + err = message.SendEmail(prompt, email, content) if err != nil { - logger.SysError("failed to send email" + err.Error()) + logger.SysError("failed to send email: " + err.Error()) } } }() diff --git a/monitor/channel.go b/monitor/channel.go index 90443965..f11c8fe1 100644 --- a/monitor/channel.go +++ b/monitor/channel.go @@ -31,18 +31,32 @@ func notifyRootUser(subject string, content string) { func DisableChannel(channelId int, channelName string, reason string) { model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled) logger.SysLog(fmt.Sprintf("channel #%d has been disabled: %s", channelId, reason)) - subject := fmt.Sprintf("Channel %s (#%d) has been disabled", channelName, channelId) - content := fmt.Sprintf("Channel %s (#%d) has been disabled, reason: %s", channelName, channelId, reason) + subject := fmt.Sprintf("Channel Status Change Reminder") + content := message.EmailTemplate( + subject, + fmt.Sprintf(` +Hello!
+Channel “%s” (#%d) has been disabled.
+Reason for disabling:
+%s
+ `, channelName, channelId, reason), + ) notifyRootUser(subject, content) } func MetricDisableChannel(channelId int, successRate float64) { model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled) logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100)) - subject := fmt.Sprintf("Channel #%d has been disabled", channelId) - content := fmt.Sprintf("The channel (#%d) has been automatically disabled by the system due to "+ - "a success rate of %.2f%% over the last %d calls, which is below the threshold of %.2f%%.", - channelId, successRate*100, config.MetricQueueSize, config.MetricSuccessRateThreshold*100) + subject := fmt.Sprintf("Channel Status Change Reminder") + content := message.EmailTemplate( + subject, + fmt.Sprintf(` +Hello!
+Channel #%d has been automatically disabled by the system.
+Reason for disabling:
+In the last %d calls, the success rate of this channel was %.2f%%, which is below the system threshold of %.2f%%.
+ `, channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100), + ) notifyRootUser(subject, content) } @@ -50,7 +64,14 @@ func MetricDisableChannel(channelId int, successRate float64) { func EnableChannel(channelId int, channelName string) { model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled) logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId)) - subject := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId) - content := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId) + subject := fmt.Sprintf("Channel Status Change Reminder") + content := message.EmailTemplate( + subject, + fmt.Sprintf(` +Hello!
+Channel “%s” (#%d) has been re-enabled.
+You can now continue using this channel.
+ `, channelName, channelId), + ) notifyRootUser(subject, content) } diff --git a/relay/adaptor/anthropic/constants.go b/relay/adaptor/anthropic/constants.go index 8ea7c4d8..9b515c1c 100644 --- a/relay/adaptor/anthropic/constants.go +++ b/relay/adaptor/anthropic/constants.go @@ -4,6 +4,7 @@ var ModelList = []string{ "claude-instant-1.2", "claude-2.0", "claude-2.1", "claude-3-haiku-20240307", "claude-3-5-haiku-20241022", + "claude-3-5-haiku-latest", "claude-3-sonnet-20240229", "claude-3-opus-20240229", "claude-3-5-sonnet-20240620", diff --git a/relay/adaptor/groq/constants.go b/relay/adaptor/groq/constants.go index 0864ebe7..4d178c8e 100644 --- a/relay/adaptor/groq/constants.go +++ b/relay/adaptor/groq/constants.go @@ -11,7 +11,6 @@ var ModelList = []string{ "llama-3.2-11b-vision-preview", "llama-3.2-1b-preview", "llama-3.2-3b-preview", - "llama-3.2-11b-vision-preview", "llama-3.2-90b-text-preview", "llama-3.2-90b-vision-preview", "llama-guard-3-8b", diff --git a/relay/adaptor/openai/token.go b/relay/adaptor/openai/token.go index c10d61ac..9e6a647b 100644 --- a/relay/adaptor/openai/token.go +++ b/relay/adaptor/openai/token.go @@ -27,7 +27,8 @@ func InitTokenEncoders() { logger.SysLog("initializing token encoders") gpt35TokenEncoder, err := tiktoken.EncodingForModel("gpt-3.5-turbo") if err != nil { - logger.FatalLog(fmt.Sprintf("failed to get gpt-3.5-turbo token encoder: %s", err.Error())) + logger.FatalLog(fmt.Sprintf("failed to get gpt-3.5-turbo token encoder: %s, "+ + "if you are using in offline environment, please set TIKTOKEN_CACHE_DIR to use exsited files, check this link for more information: https://stackoverflow.com/questions/76106366/how-to-use-tiktoken-in-offline-mode-computer ", err.Error())) } defaultTokenEncoder = gpt35TokenEncoder gpt4oTokenEncoder, err := tiktoken.EncodingForModel("gpt-4o") diff --git a/relay/adaptor/zhipu/constants.go b/relay/adaptor/zhipu/constants.go index e1192123..a86ffc42 100644 --- a/relay/adaptor/zhipu/constants.go +++ b/relay/adaptor/zhipu/constants.go @@ -1,7 +1,14 @@ package zhipu +// https://open.bigmodel.cn/pricing + var ModelList = []string{ - "chatglm_turbo", "chatglm_pro", "chatglm_std", "chatglm_lite", - "glm-4", "glm-4v", "glm-3-turbo", "embedding-2", - "cogview-3", + "glm-zero-preview", "glm-4-plus", "glm-4-0520", "glm-4-airx", + "glm-4-air", "glm-4-long", "glm-4-flashx", "glm-4-flash", + "glm-4", "glm-3-turbo", + "glm-4v-plus", "glm-4v", "glm-4v-flash", + "cogview-3-plus", "cogview-3", "cogview-3-flash", + "cogviewx", "cogviewx-flash", + "charglm-4", "emohaa", "codegeex-4", + "embedding-2", "embedding-3", } diff --git a/relay/billing/ratio/model.go b/relay/billing/ratio/model.go index 237001ad..6d1e4482 100644 --- a/relay/billing/ratio/model.go +++ b/relay/billing/ratio/model.go @@ -98,6 +98,7 @@ var ModelRatio = map[string]float64{ "claude-2.1": 8.0 / 1000 * USD, "claude-3-haiku-20240307": 0.25 / 1000 * USD, "claude-3-5-haiku-20241022": 1.0 / 1000 * USD, + "claude-3-5-haiku-latest": 1.0 / 1000 * USD, "claude-3-sonnet-20240229": 3.0 / 1000 * USD, "claude-3-5-sonnet-20240620": 3.0 / 1000 * USD, "claude-3-5-sonnet-20241022": 3.0 / 1000 * USD, @@ -131,15 +132,29 @@ var ModelRatio = map[string]float64{ "gemini-2.0-flash-thinking-exp-01-21": 0.075 * MILLI_USD, "aqa": 1, // https://open.bigmodel.cn/pricing - "glm-4": 0.1 * RMB, - "glm-4v": 0.1 * RMB, - "glm-3-turbo": 0.005 * RMB, - "embedding-2": 0.0005 * RMB, - "chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens - "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens - "chatglm_std": 0.3572, // ¥0.005 / 1k tokens - "chatglm_lite": 0.1429, // ¥0.002 / 1k tokens - "cogview-3": 0.25 * RMB, + "glm-zero-preview": 0.01 * RMB, + "glm-4-plus": 0.05 * RMB, + "glm-4-0520": 0.1 * RMB, + "glm-4-airx": 0.01 * RMB, + "glm-4-air": 0.0005 * RMB, + "glm-4-long": 0.001 * RMB, + "glm-4-flashx": 0.0001 * RMB, + "glm-4-flash": 0, + "glm-4": 0.1 * RMB, // deprecated model, available until 2025/06 + "glm-3-turbo": 0.001 * RMB, // deprecated model, available until 2025/06 + "glm-4v-plus": 0.004 * RMB, + "glm-4v": 0.05 * RMB, + "glm-4v-flash": 0, + "cogview-3-plus": 0.06 * RMB, + "cogview-3": 0.1 * RMB, + "cogview-3-flash": 0, + "cogviewx": 0.5 * RMB, + "cogviewx-flash": 0, + "charglm-4": 0.001 * RMB, + "emohaa": 0.015 * RMB, + "codegeex-4": 0.0001 * RMB, + "embedding-2": 0.0005 * RMB, + "embedding-3": 0.0005 * RMB, // https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-thousand-questions-metering-and-billing "qwen-turbo": 1.4286, // ¥0.02 / 1k tokens "qwen-turbo-latest": 1.4286, @@ -226,9 +241,19 @@ var ModelRatio = map[string]float64{ "embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens "embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens "semantic_similarity_s1_v1": 0.0715, // ¥0.001 / 1k tokens - "hunyuan": 7.143, // ¥0.1 / 1k tokens // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0 - "ChatStd": 0.01 * RMB, - "ChatPro": 0.1 * RMB, + // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0 + "hunyuan-turbo": 0.015 * RMB, + "hunyuan-large": 0.004 * RMB, + "hunyuan-large-longcontext": 0.006 * RMB, + "hunyuan-standard": 0.0008 * RMB, + "hunyuan-standard-256K": 0.0005 * RMB, + "hunyuan-translation-lite": 0.005 * RMB, + "hunyuan-role": 0.004 * RMB, + "hunyuan-functioncall": 0.004 * RMB, + "hunyuan-code": 0.004 * RMB, + "hunyuan-turbo-vision": 0.08 * RMB, + "hunyuan-vision": 0.018 * RMB, + "hunyuan-embedding": 0.0007 * RMB, // https://platform.moonshot.cn/pricing "moonshot-v1-8k": 0.012 * RMB, "moonshot-v1-32k": 0.024 * RMB, diff --git a/relay/client/init.go b/relay/client/init.go index 73108700..ddc2a785 100644 --- a/relay/client/init.go +++ b/relay/client/init.go @@ -5,7 +5,7 @@ import ( "os" "time" - gutils "github.com/Laisky/go-utils/v4" + gutils "github.com/Laisky/go-utils/v5" "github.com/songquanpeng/one-api/common/config" ) diff --git a/web/default/package.json b/web/default/package.json index a0bdb267..9078f14b 100644 --- a/web/default/package.json +++ b/web/default/package.json @@ -7,8 +7,8 @@ "history": "^5.3.0", "i18next": "23.2.3", "i18next-browser-languagedetector": "^8.0.2", - "i18next-http-backend": "^3.0.2", "marked": "^4.1.1", + "moment": "^2.30.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", diff --git a/web/default/public/locales/en/translation.json b/web/default/public/locales/en/translation.json deleted file mode 100644 index baab810b..00000000 --- a/web/default/public/locales/en/translation.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "header": { - "home": "Home", - "channel": "Channel", - "token": "Token", - "redemption": "Redemption", - "topup": "Top Up", - "user": "User", - "dashboard": "Dashboard", - "log": "Log", - "setting": "Settings", - "about": "About", - "chat": "Chat", - "login": "Login", - "logout": "Logout", - "register": "Register" - }, - "topup": { - "title": "Top Up Center", - "get_code": { - "title": "Get Redemption Code", - "current_quota": "Current Available Quota", - "button": "Get Code Now" - }, - "redeem_code": { - "title": "Redeem Code", - "placeholder": "Please enter redemption code", - "paste": "Paste", - "paste_error": "Cannot access clipboard, please paste manually", - "submit": "Redeem Now", - "submitting": "Redeeming...", - "empty_code": "Please enter the redemption code!", - "success": "Top up successful!", - "request_failed": "Request failed", - "no_link": "Admin has not set up the top-up link!" - } - }, - "channel": { - "title": "Channel Management", - "search": "Search channels by ID, name and key...", - "balance_notice": "OpenAI channels no longer support getting balance via key, so balance shows as 0. For supported channel types, click balance to refresh.", - "test_notice": "Channel testing only supports chat models, preferring gpt-3.5-turbo. If unavailable, uses the first model in your configured list.", - "detail_notice": "Click the detail button below to show balance and set additional test models.", - "table": { - "id": "ID", - "name": "Name", - "group": "Group", - "type": "Type", - "status": "Status", - "response_time": "Response Time", - "balance": "Balance", - "priority": "Priority", - "test_model": "Test Model", - "actions": "Actions", - "no_name": "None", - "status_enabled": "Enabled", - "status_disabled": "Disabled", - "status_auto_disabled": "Disabled", - "status_disabled_tip": "This channel is manually disabled", - "status_auto_disabled_tip": "This channel is automatically disabled", - "status_unknown": "Unknown Status", - "not_tested": "Not Tested", - "priority_tip": "Channel selection priority, higher is preferred", - "select_test_model": "Please select test model", - "click_to_update": "Click to update" - }, - "buttons": { - "test": "Test", - "delete": "Delete", - "confirm_delete": "Delete Channel", - "enable": "Enable", - "disable": "Disable", - "edit": "Edit", - "add": "Add New Channel", - "test_all": "Test All Channels", - "test_disabled": "Test Disabled Channels", - "delete_disabled": "Delete Disabled Channels", - "confirm_delete_disabled": "Confirm Delete", - "refresh": "Refresh", - "show_detail": "Details", - "hide_detail": "Hide Details" - }, - "messages": { - "test_success": "Channel ${name} test successful, model ${model}, time ${time}s, output: ${message}", - "test_all_started": "Channel testing started successfully, please refresh page to see results.", - "delete_disabled_success": "Deleted all disabled channels, total: ${count}", - "balance_update_success": "Channel ${name} balance updated successfully!", - "all_balance_updated": "All enabled channel balances have been updated!" - }, - "edit": { - "title_edit": "Update Channel Information", - "title_create": "Create New Channel", - "type": "Type", - "name": "Name", - "name_placeholder": "Please enter name", - "group": "Group", - "group_placeholder": "Please select groups that can use this channel", - "group_addition": "Please edit group multipliers in system settings to add new group:", - "models": "Models", - "models_placeholder": "Please select models supported by this channel", - "model_mapping": "Model Mapping", - "model_mapping_placeholder": "Optional, used to modify model names in request body. A JSON string where keys are request model names and values are target model names", - "system_prompt": "System Prompt", - "system_prompt_placeholder": "Optional, used to force set system prompt. Use with custom model & model mapping. First create a unique custom model name above, then map it to a natively supported model", - "base_url": "Proxy", - "base_url_placeholder": "Optional, used for API calls through proxy. Enter proxy address in format: https://domain.com", - "key": "Key", - "key_placeholder": "Please enter key", - "batch": "Batch Create", - "batch_placeholder": "Please enter keys, one per line", - "buttons": { - "cancel": "Cancel", - "submit": "Submit", - "fill_models": "Fill Related Models", - "fill_all": "Fill All Models", - "clear": "Clear All Models", - "add_custom": "Add", - "custom_placeholder": "Enter custom model name" - }, - "messages": { - "name_required": "Please enter channel name and key!", - "models_required": "Please select at least one model!", - "model_mapping_invalid": "Model mapping must be valid JSON format!", - "update_success": "Channel updated successfully!", - "create_success": "Channel created successfully!" - }, - "spark_version": "Model Version", - "spark_version_placeholder": "Please enter Spark model version from API URL, e.g.: v2.1", - "knowledge_id": "Knowledge Base ID", - "knowledge_id_placeholder": "Please enter knowledge base ID, e.g.: 123456", - "plugin_param": "Plugin Parameter", - "plugin_param_placeholder": "Please enter plugin parameter (X-DashScope-Plugin header value)", - "coze_notice": "For Coze, model name is the Bot ID. You can add prefix `bot-`, e.g.: `bot-123456`.", - "douban_notice": "For Douban, you need to go to", - "douban_notice_link": "Model Inference Page", - "douban_notice_2": "to create an inference endpoint, and use the endpoint name as model name, e.g.: `ep-20240608051426-tkxvl`.", - "aws_region_placeholder": "region, e.g.: us-west-2", - "aws_ak_placeholder": "AWS IAM Access Key", - "aws_sk_placeholder": "AWS IAM Secret Key", - "vertex_region_placeholder": "Vertex AI Region, e.g.: us-east5", - "vertex_project_id": "Vertex AI Project ID", - "vertex_project_id_placeholder": "Vertex AI Project ID", - "vertex_credentials": "Google Cloud Application Default Credentials JSON", - "vertex_credentials_placeholder": "Google Cloud Application Default Credentials JSON", - "user_id": "User ID", - "user_id_placeholder": "User ID who generated this key", - "key_prompts": { - "default": "Please enter the authentication key for this channel", - "zhipu": "Enter in format: APIKey|SecretKey", - "spark": "Enter in format: APPID|APISecret|APIKey", - "fastgpt": "Enter in format: APIKey-AppId, e.g.: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", - "tencent": "Enter in format: AppId|SecretId|SecretKey" - } - } - } -} diff --git a/web/default/public/locales/zh/translation.json b/web/default/public/locales/zh/translation.json deleted file mode 100644 index 8b53e005..00000000 --- a/web/default/public/locales/zh/translation.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "header": { - "home": "首页", - "channel": "渠道", - "token": "令牌", - "redemption": "兑换", - "topup": "充值", - "user": "用户", - "dashboard": "总览", - "log": "日志", - "setting": "设置", - "about": "关于", - "chat": "聊天", - "login": "登录", - "logout": "注销", - "register": "注册" - }, - "topup": { - "title": "充值中心", - "get_code": { - "title": "获取兑换码", - "current_quota": "当前可用额度", - "button": "立即获取兑换码" - }, - "redeem_code": { - "title": "兑换码充值", - "placeholder": "请输入兑换码", - "paste": "粘贴", - "paste_error": "无法访问剪贴板,请手动粘贴", - "submit": "立即兑换", - "submitting": "兑换中...", - "empty_code": "请输入兑换码!", - "success": "充值成功!", - "request_failed": "请求失败", - "no_link": "超级管理员未设置充值链接!" - } - }, - "channel": { - "title": "管理渠道", - "search": "搜索渠道的 ID,名称和密钥 ...", - "balance_notice": "OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 0。对于支持的渠道类型,请点击余额进行刷新。", - "test_notice": "渠道测试仅支持 chat 模型,优先使用 gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。", - "detail_notice": "点击下方详情按钮可以显示余额以及设置额外的测试模型。", - "table": { - "id": "ID", - "name": "名称", - "group": "分组", - "type": "类型", - "status": "状态", - "response_time": "响应时间", - "balance": "余额", - "priority": "优先级", - "test_model": "测试模型", - "actions": "操作", - "no_name": "无", - "status_enabled": "已启用", - "status_disabled": "已禁用", - "status_auto_disabled": "已禁用", - "status_disabled_tip": "本渠道被手动禁用", - "status_auto_disabled_tip": "本渠道被程序自动禁用", - "status_unknown": "未知状态", - "not_tested": "未测试", - "priority_tip": "渠道选择优先级,越高越优先", - "select_test_model": "请选择测试模型", - "click_to_update": "点击更新" - }, - "buttons": { - "test": "测试", - "delete": "删除", - "confirm_delete": "删除渠道", - "enable": "启用", - "disable": "禁用", - "edit": "编辑", - "add": "添加新的渠道", - "test_all": "测试所有渠道", - "test_disabled": "测试禁用渠道", - "delete_disabled": "删除禁用渠道", - "confirm_delete_disabled": "确认删除", - "refresh": "刷新", - "show_detail": "详情", - "hide_detail": "隐藏详情" - }, - "messages": { - "test_success": "渠道 ${name} 测试成功,模型 ${model},耗时 ${time} 秒,模型输出:${message}", - "test_all_started": "已成功开始测试渠道,请刷新页面查看结果。", - "delete_disabled_success": "已删除所有禁用渠道,共计 ${count} 个", - "balance_update_success": "渠道 ${name} 余额更新成功!", - "all_balance_updated": "已更新完毕所有已启用渠道余额!" - }, - "edit": { - "title_edit": "更新渠道信息", - "title_create": "创建新的渠道", - "type": "类型", - "name": "名称", - "name_placeholder": "请输入名称", - "group": "分组", - "group_placeholder": "请选择可以使用该渠道的分组", - "group_addition": "请在系统设置页面编辑分组倍率以添加新的分组:", - "models": "模型", - "models_placeholder": "请选择该渠道所支持的模型", - "model_mapping": "模型重定向", - "model_mapping_placeholder": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称", - "system_prompt": "系统提示词", - "system_prompt_placeholder": "此项可选,用于强制设置给定的系统提示词,请配合自定义模型 & 模型重定向使用,首先创建一个唯一的自定义模型名称并在上面填入,之后将该自定义模型重定向映射到该渠道一个原生支持的模型", - "base_url": "代理", - "base_url_placeholder": "此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com", - "key": "密钥", - "key_placeholder": "请输入密钥", - "batch": "批量创建", - "batch_placeholder": "请输入密钥,一行一个", - "buttons": { - "cancel": "取消", - "submit": "提交", - "fill_models": "填入相关模型", - "fill_all": "填入所有模型", - "clear": "清除所有模型", - "add_custom": "填入", - "custom_placeholder": "输入自定义模型名称" - }, - "messages": { - "name_required": "请填写渠道名称和渠道密钥!", - "models_required": "请至少选择一个模型!", - "model_mapping_invalid": "模型映射必须是合法的 JSON 格式!", - "update_success": "渠道更新成功!", - "create_success": "渠道创建成功!" - }, - "spark_version": "模型版本", - "spark_version_placeholder": "请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1", - "knowledge_id": "知识库 ID", - "knowledge_id_placeholder": "请输入知识库 ID,例如:123456", - "plugin_param": "插件参数", - "plugin_param_placeholder": "请输入插件参数,即 X-DashScope-Plugin 请求头的取值", - "coze_notice": "对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀 `bot-`,例如:`bot-123456`。", - "douban_notice": "对于豆包而言,需要手动去", - "douban_notice_link": "模型推理页面", - "douban_notice_2": "创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。", - "aws_region_placeholder": "region,例如:us-west-2", - "aws_ak_placeholder": "AWS IAM Access Key", - "aws_sk_placeholder": "AWS IAM Secret Key", - "vertex_region_placeholder": "Vertex AI Region,例如:us-east5", - "vertex_project_id": "Vertex AI Project ID", - "vertex_project_id_placeholder": "Vertex AI Project ID", - "vertex_credentials": "Google Cloud Application Default Credentials JSON", - "vertex_credentials_placeholder": "Google Cloud Application Default Credentials JSON", - "user_id": "User ID", - "user_id_placeholder": "生成该密钥的用户 ID", - "key_prompts": { - "default": "请输入渠道对应的鉴权密钥", - "zhipu": "按照如下格式输入:APIKey|SecretKey", - "spark": "按照如下格式输入:APPID|APISecret|APIKey", - "fastgpt": "按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", - "tencent": "按照如下格式输入:AppId|SecretId|SecretKey" - } - } - } -} diff --git a/web/default/src/App.js b/web/default/src/App.js index 4db38c82..1ea57ebe 100644 --- a/web/default/src/App.js +++ b/web/default/src/App.js @@ -42,32 +42,37 @@ function App() { } }; const loadStatus = async () => { - const res = await API.get('/api/status'); - const { success, data } = res.data; - if (success) { - localStorage.setItem('status', JSON.stringify(data)); - statusDispatch({ type: 'set', payload: data }); - localStorage.setItem('system_name', data.system_name); - localStorage.setItem('logo', data.logo); - localStorage.setItem('footer_html', data.footer_html); - localStorage.setItem('quota_per_unit', data.quota_per_unit); - localStorage.setItem('display_in_currency', data.display_in_currency); - if (data.chat_link) { - localStorage.setItem('chat_link', data.chat_link); + try { + const res = await API.get('/api/status'); + const { success, message, data } = res.data || {}; // Add default empty object + if (success && data) { + // Check data exists + localStorage.setItem('status', JSON.stringify(data)); + statusDispatch({ type: 'set', payload: data }); + localStorage.setItem('system_name', data.system_name); + localStorage.setItem('logo', data.logo); + localStorage.setItem('footer_html', data.footer_html); + localStorage.setItem('quota_per_unit', data.quota_per_unit); + localStorage.setItem('display_in_currency', data.display_in_currency); + if (data.chat_link) { + localStorage.setItem('chat_link', data.chat_link); + } else { + localStorage.removeItem('chat_link'); + } + if ( + data.version !== process.env.REACT_APP_VERSION && + data.version !== 'v0.0.0' && + process.env.REACT_APP_VERSION !== '' + ) { + showNotice( + `New version available: ${data.version}, please refresh the page using Shift + F5` + ); + } } else { - localStorage.removeItem('chat_link'); + showError(message || 'Unable to connect to the server properly!'); } - if ( - data.version !== process.env.REACT_APP_VERSION && - data.version !== 'v0.0.0' && - process.env.REACT_APP_VERSION !== '' - ) { - showNotice( - `New version available: ${data.version}, please refresh the page using the shortcut key Shift + F5` - ); - } - } else { - showError('Unable to connect to the server normally!'); + } catch (error) { + showError(error.message || 'Unable to connect to the server properly!'); } }; diff --git a/web/default/src/components/ChannelsTable.js b/web/default/src/components/ChannelsTable.js index 7f667858..dd1aaab5 100644 --- a/web/default/src/components/ChannelsTable.js +++ b/web/default/src/components/ChannelsTable.js @@ -198,7 +198,7 @@ const ChannelsTable = () => { } const { success, message } = res.data; if (success) { - showSuccess('Operation successfully completed!'); + showSuccess(t('channel.messages.operation_success')); let channel = res.data.data; let newChannels = [...channels]; let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx; @@ -325,13 +325,8 @@ const ChannelsTable = () => { newChannels[realIdx].response_time = time * 1000; newChannels[realIdx].test_time = Date.now() / 1000; setChannels(newChannels); - showInfo( - t('channel.messages.test_success', { - name: name, - model: model, - time: time.toFixed(2), - message: message, - }) + showSuccess( + t('channel.messages.test_success', { name, model, time, message }) ); } else { showError(message); @@ -375,7 +370,7 @@ const ChannelsTable = () => { newChannels[realIdx].balance = balance; newChannels[realIdx].balance_updated_time = Date.now() / 1000; setChannels(newChannels); - showInfo(t('channel.messages.balance_update_success', { name: name })); + showSuccess(t('channel.messages.balance_update_success', { name })); } else { showError(message); } @@ -594,13 +589,13 @@ const ChannelsTable = () => { defaultValue={channel.test_model} onChange={(event, data) => { switchTestModel(idx, data.value); - }} - /> - -- Scan the QR code with WeChat, follow the official account and enter 'verification code' to get the verification code (valid within three minutes) -
+{t('auth.login.wechat.scan_tip')}
- A new password has been generated. Please click the password box or the button above to copy it. Please log in and change your password promptly! + {t('auth.reset.confirm.notice')}
- The system will send an email with a reset link to your email address. Please check your inbox. + {t('auth.reset.notice')}
- Scan the QR code with WeChat, follow the official account and enter 'verification code' to get the verification code (valid within three minutes) -
+{t('setting.personal.binding.wechat.description')}
Canceling password login will cause all users (including administrators) who have not bound other login methods to be unable to log in via password, confirm cancel?
+{t('setting.system.password_login.warning.content')}
{inputs.ServerAddress}
- , Fill in the Authorization callback URL{' '}
- {`${inputs.ServerAddress}/oauth/github`}
+ {t('setting.system.github.subtitle')}
+
+ {t('setting.system.github.manage_link')}
+
+ {t('setting.system.github.manage_text')}
+ {inputs.ServerAddress}
- , Fill in the Redirect URL{' '}
- {`${inputs.ServerAddress}/oauth/lark`}
+ {t('setting.system.lark.url_notice', {
+ server_url: inputs.ServerAddress,
+ callback_url: `${inputs.ServerAddress}/oauth/lark`,
+ })}
You can set the about content on the settings page, supporting HTML & Markdown
- Project repository address: - - https://github.com/Laisky/one-api +{t('about.description')}
+ {t('about.repository')} + + https://github.com/songquanpeng/one-api