mirror of
https://github.com/songquanpeng/one-api.git
synced 2026-04-14 14:04:28 +08:00
Merge branch 'main' into patch/gpt-4o-audio
This commit is contained in:
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
72
common/i18n/i18n.go
Normal 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
|
||||
}
|
||||
5
common/i18n/locales/en.json
Normal file
5
common/i18n/locales/en.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"invalid_input": "Invalid input, please check your input",
|
||||
"send_email_failed": "failed to send email: ",
|
||||
"invalid_parameter": "invalid parameter"
|
||||
}
|
||||
5
common/i18n/locales/zh-CN.json
Normal file
5
common/i18n/locales/zh-CN.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"invalid_input": "无效的输入,请检查您的输入",
|
||||
"send_email_failed": "发送邮件失败:",
|
||||
"invalid_parameter": "无效的参数"
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
34
common/message/template.go
Normal file
34
common/message/template.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user