Merge branch 'main' into patch/gpt-4o-audio

This commit is contained in:
JustSong
2025-02-02 22:03:27 +08:00
committed by GitHub
85 changed files with 6739 additions and 3352 deletions

View File

@@ -1,13 +1,14 @@
package config
import (
"github.com/songquanpeng/one-api/common/env"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/songquanpeng/one-api/common/env"
"github.com/google/uuid"
)
@@ -125,10 +126,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
@@ -163,3 +164,4 @@ var UserContentRequestTimeout = env.Int("USER_CONTENT_REQUEST_TIMEOUT", 30)
// EnforceIncludeUsage is used to determine whether to include usage in the response
var EnforceIncludeUsage = env.Bool("ENFORCE_INCLUDE_USAGE", false)
var TestPrompt = env.String("TEST_PROMPT", "Print your model name exactly and do not output without any other text.")

View File

@@ -1,9 +1,8 @@
package helper
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/random"
"html/template"
"log"
"net"
@@ -11,6 +10,10 @@ import (
"runtime"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/random"
)
func OpenBrowser(url string) {
@@ -106,6 +109,18 @@ func GenRequestID() string {
return GetTimeString() + random.GetRandomNumberString(8)
}
func SetRequestID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, RequestIdKey, id)
}
func GetRequestID(ctx context.Context) string {
rawRequestId := ctx.Value(RequestIdKey)
if rawRequestId == nil {
return ""
}
return rawRequestId.(string)
}
func GetResponseID(c *gin.Context) string {
logID := c.GetString(RequestIdKey)
return fmt.Sprintf("chatcmpl-%s", logID)

View File

@@ -13,3 +13,8 @@ func GetTimeString() string {
now := time.Now()
return fmt.Sprintf("%s%d", now.Format("20060102150405"), now.UnixNano()%1e9)
}
// CalcElapsedTime return the elapsed time in milliseconds (ms)
func CalcElapsedTime(start time.Time) int64 {
return time.Now().Sub(start).Milliseconds()
}

72
common/i18n/i18n.go Normal file
View File

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

View File

@@ -0,0 +1,5 @@
{
"invalid_input": "Invalid input, please check your input",
"send_email_failed": "failed to send email: ",
"invalid_parameter": "invalid parameter"
}

View File

@@ -0,0 +1,5 @@
{
"invalid_input": "无效的输入,请检查您的输入",
"send_email_failed": "发送邮件失败:",
"invalid_parameter": "无效的参数"
}

View File

@@ -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)
}
@@ -113,16 +121,16 @@ func logHelper(ctx context.Context, level loggerLevel, msg string) {
if level == loggerINFO {
writer = gin.DefaultWriter
}
var logId string
var requestId string
if ctx != nil {
rawLogId := ctx.Value(helper.RequestIdKey)
if rawLogId != nil {
logId = fmt.Sprintf(" | %s", rawLogId.(string))
rawRequestId := helper.GetRequestID(ctx)
if rawRequestId != "" {
requestId = fmt.Sprintf(" | %s", rawRequestId)
}
}
lineInfo, funcName := getLineInfo()
now := time.Now()
_, _ = fmt.Fprintf(writer, "[%s] %v%s%s %s%s \n", level, now.Format("2006/01/02 - 15:04:05"), logId, lineInfo, funcName, msg)
_, _ = fmt.Fprintf(writer, "[%s] %v%s%s %s%s \n", level, now.Format("2006/01/02 - 15:04:05"), requestId, lineInfo, funcName, msg)
SetupLogger()
if level == loggerFatal {
os.Exit(1)

View File

@@ -5,11 +5,13 @@ import (
"crypto/tls"
"encoding/base64"
"fmt"
"github.com/songquanpeng/one-api/common/config"
"net"
"net/smtp"
"strings"
"time"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger"
)
func shouldAuth() bool {
@@ -98,8 +100,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
}

View File

@@ -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(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="margin: 0; padding: 20px; font-family: Arial, sans-serif; line-height: 1.6; background-color: #f4f4f4;">
<div style="max-width: 600px; margin: 20px auto; padding: 30px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div style="text-align: center; margin-bottom: 30px;">
<h2 style="color: #333; margin: 0; font-size: 24px;">%s</h2>
</div>
<div style="color: #555; font-size: 16px;">
%s
</div>
<div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #eee; color: #888; font-size: 14px; text-align: center;">
<p style="margin: 5px 0;">此邮件由系统自动发送,请勿直接回复</p>
<p style="margin: 5px 0;">%s</p>
</div>
</div>
</body>
</html>
`, title, content, config.SystemName)
}