|
|
package controller |
|
|
|
|
|
import ( |
|
|
"encoding/json" |
|
|
"fmt" |
|
|
"genspark2api/common" |
|
|
"genspark2api/common/config" |
|
|
logger "genspark2api/common/loggger" |
|
|
"io/ioutil" |
|
|
"net/http" |
|
|
"os" |
|
|
"strings" |
|
|
"time" |
|
|
|
|
|
"github.com/gin-gonic/gin" |
|
|
) |
|
|
|
|
|
|
|
|
var startTime = time.Now() |
|
|
|
|
|
|
|
|
type GSCookie struct { |
|
|
Value string |
|
|
RequestCount int |
|
|
FailCount int |
|
|
LastUsedTime time.Time |
|
|
Disabled bool |
|
|
} |
|
|
|
|
|
|
|
|
var cookieStats = make(map[string]*GSCookie) |
|
|
|
|
|
|
|
|
type ModelInfo struct { |
|
|
ID string `json:"id"` |
|
|
Name string `json:"name"` |
|
|
Category string `json:"category"` |
|
|
Enabled bool `json:"enabled"` |
|
|
} |
|
|
|
|
|
|
|
|
type ModelsListRequest struct { |
|
|
Models []ModelInfo `json:"models"` |
|
|
} |
|
|
|
|
|
|
|
|
var modelsList []ModelInfo |
|
|
var modelsListFile = "./data/models_list.json" |
|
|
|
|
|
|
|
|
func init() { |
|
|
|
|
|
loadModelsList() |
|
|
} |
|
|
|
|
|
|
|
|
func AdminIndex(c *gin.Context) { |
|
|
c.File("./public/index.html") |
|
|
} |
|
|
|
|
|
|
|
|
func GetConfig(c *gin.Context) { |
|
|
|
|
|
configData := map[string]string{ |
|
|
"GS_COOKIE": os.Getenv("GS_COOKIE"), |
|
|
"API_SECRET": os.Getenv("API_SECRET"), |
|
|
"AUTO_MODEL_CHAT_MAP_TYPE": os.Getenv("AUTO_MODEL_CHAT_MAP_TYPE"), |
|
|
"AUTO_DEL_CHAT": os.Getenv("AUTO_DEL_CHAT"), |
|
|
"MODEL_CHAT_MAP": os.Getenv("MODEL_CHAT_MAP"), |
|
|
"PROXY_URL": os.Getenv("PROXY_URL"), |
|
|
"RECAPTCHA_PROXY_URL": os.Getenv("RECAPTCHA_PROXY_URL"), |
|
|
"REQUEST_RATE_LIMIT": os.Getenv("REQUEST_RATE_LIMIT"), |
|
|
"RATE_LIMIT_COOKIE_LOCK_DURATION": os.Getenv("RATE_LIMIT_COOKIE_LOCK_DURATION"), |
|
|
"ROUTE_PREFIX": os.Getenv("ROUTE_PREFIX"), |
|
|
"DEBUG": os.Getenv("DEBUG"), |
|
|
"REASONING_HIDE": os.Getenv("REASONING_HIDE"), |
|
|
} |
|
|
|
|
|
c.JSON(http.StatusOK, configData) |
|
|
} |
|
|
|
|
|
|
|
|
func SaveConfig(c *gin.Context) { |
|
|
|
|
|
var configData map[string]string |
|
|
if err := c.ShouldBindJSON(&configData); err != nil { |
|
|
c.JSON(http.StatusBadRequest, gin.H{ |
|
|
"success": false, |
|
|
"message": "无效的请求数据", |
|
|
}) |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
envFilePath := "./data/.env" |
|
|
envLines := []string{} |
|
|
envMap := make(map[string]string) |
|
|
|
|
|
|
|
|
if err := os.MkdirAll("./data", 0755); err != nil { |
|
|
logger.SysLog(fmt.Sprintf("创建data目录失败: %s", err.Error())) |
|
|
} |
|
|
|
|
|
if fileData, err := ioutil.ReadFile(envFilePath); err == nil { |
|
|
content := string(fileData) |
|
|
lines := strings.Split(content, "\n") |
|
|
for _, line := range lines { |
|
|
line = strings.TrimSpace(line) |
|
|
if line != "" && !strings.HasPrefix(line, "#") { |
|
|
parts := strings.SplitN(line, "=", 2) |
|
|
if len(parts) == 2 { |
|
|
key := strings.TrimSpace(parts[0]) |
|
|
value := strings.TrimSpace(parts[1]) |
|
|
envMap[key] = value |
|
|
} else { |
|
|
envLines = append(envLines, line) |
|
|
} |
|
|
} else { |
|
|
envLines = append(envLines, line) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for key, value := range configData { |
|
|
if value != "" { |
|
|
envMap[key] = value |
|
|
|
|
|
os.Setenv(key, value) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var newContent strings.Builder |
|
|
|
|
|
|
|
|
for _, line := range envLines { |
|
|
if !strings.Contains(line, "=") { |
|
|
newContent.WriteString(line) |
|
|
newContent.WriteString("\n") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for key, value := range envMap { |
|
|
newContent.WriteString(fmt.Sprintf("%s=%s\n", key, value)) |
|
|
} |
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(envFilePath, []byte(newContent.String()), 0644); err != nil { |
|
|
c.JSON(http.StatusInternalServerError, gin.H{ |
|
|
"success": false, |
|
|
"message": "保存配置失败: " + err.Error(), |
|
|
}) |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
if _, ok := configData["GS_COOKIE"]; ok { |
|
|
config.InitGSCookies() |
|
|
|
|
|
updateCookieStats() |
|
|
} |
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"success": true, |
|
|
"message": "配置已保存", |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
func updateCookieStats() { |
|
|
|
|
|
currentCookies := config.GetGSCookies() |
|
|
|
|
|
|
|
|
for _, cookie := range currentCookies { |
|
|
if _, exists := cookieStats[cookie]; !exists { |
|
|
cookieStats[cookie] = &GSCookie{ |
|
|
Value: cookie, |
|
|
RequestCount: 0, |
|
|
FailCount: 0, |
|
|
LastUsedTime: time.Now(), |
|
|
Disabled: false, |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for cookie := range cookieStats { |
|
|
exists := false |
|
|
for _, currentCookie := range currentCookies { |
|
|
if cookie == currentCookie { |
|
|
exists = true |
|
|
break |
|
|
} |
|
|
} |
|
|
if !exists { |
|
|
delete(cookieStats, cookie) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func GetStatus(c *gin.Context) { |
|
|
|
|
|
uptime := time.Since(startTime).String() |
|
|
|
|
|
|
|
|
updateCookieStats() |
|
|
|
|
|
|
|
|
cookieCount := len(config.GetGSCookies()) |
|
|
cookies := make([]map[string]interface{}, 0, cookieCount) |
|
|
|
|
|
|
|
|
for cookie, stats := range cookieStats { |
|
|
cookieInfo := map[string]interface{}{ |
|
|
"value": cookie, |
|
|
"active": !stats.Disabled, |
|
|
"requestCount": stats.RequestCount, |
|
|
"lastUsed": stats.LastUsedTime.Format("2006-01-02 15:04:05"), |
|
|
} |
|
|
cookies = append(cookies, cookieInfo) |
|
|
} |
|
|
|
|
|
|
|
|
totalRequests := 0 |
|
|
successRequests := 0 |
|
|
failedRequests := 0 |
|
|
|
|
|
for _, stats := range cookieStats { |
|
|
totalRequests += stats.RequestCount |
|
|
successRequests += (stats.RequestCount - stats.FailCount) |
|
|
failedRequests += stats.FailCount |
|
|
} |
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"version": common.Version, |
|
|
"uptime": uptime, |
|
|
"cookieCount": cookieCount, |
|
|
"totalRequests": totalRequests, |
|
|
"successRequests": successRequests, |
|
|
"failedRequests": failedRequests, |
|
|
"cookies": cookies, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
func GetModels(c *gin.Context) { |
|
|
|
|
|
if len(modelsList) == 0 { |
|
|
initializeModelsFromDefaults() |
|
|
} |
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"models": modelsList, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
func SaveModels(c *gin.Context) { |
|
|
var request ModelsListRequest |
|
|
if err := c.ShouldBindJSON(&request); err != nil { |
|
|
c.JSON(http.StatusBadRequest, gin.H{ |
|
|
"success": false, |
|
|
"message": "无效的请求数据", |
|
|
}) |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
modelsList = request.Models |
|
|
|
|
|
|
|
|
if err := saveModelsToFile(); err != nil { |
|
|
c.JSON(http.StatusInternalServerError, gin.H{ |
|
|
"success": false, |
|
|
"message": "保存模型列表失败: " + err.Error(), |
|
|
}) |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
updateDefaultOpenaiModelList() |
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"success": true, |
|
|
"message": "模型列表已保存", |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
func initializeModelsFromDefaults() { |
|
|
|
|
|
modelsList = []ModelInfo{} |
|
|
|
|
|
|
|
|
for _, model := range common.TextModelList { |
|
|
modelsList = append(modelsList, ModelInfo{ |
|
|
ID: model, |
|
|
Name: getModelDisplayName(model), |
|
|
Category: "text", |
|
|
Enabled: true, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
for _, model := range common.ImageModelList { |
|
|
modelsList = append(modelsList, ModelInfo{ |
|
|
ID: model, |
|
|
Name: getModelDisplayName(model), |
|
|
Category: "image", |
|
|
Enabled: true, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
for _, model := range common.VideoModelList { |
|
|
modelsList = append(modelsList, ModelInfo{ |
|
|
ID: model, |
|
|
Name: getModelDisplayName(model), |
|
|
Category: "video", |
|
|
Enabled: true, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
saveModelsToFile() |
|
|
} |
|
|
|
|
|
|
|
|
func getModelDisplayName(modelID string) string { |
|
|
|
|
|
displayName := modelID |
|
|
|
|
|
|
|
|
parts := strings.Split(displayName, "-") |
|
|
for i, part := range parts { |
|
|
if len(part) > 0 { |
|
|
|
|
|
if part == "gpt" || part == "dall" { |
|
|
parts[i] = strings.ToUpper(part) |
|
|
} else { |
|
|
parts[i] = strings.ToUpper(part[:1]) + part[1:] |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
displayName = strings.Join(parts, " ") |
|
|
displayName = strings.Replace(displayName, "/", " ", -1) |
|
|
|
|
|
return displayName |
|
|
} |
|
|
|
|
|
|
|
|
func saveModelsToFile() error { |
|
|
|
|
|
if err := os.MkdirAll("./data", 0755); err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
|
|
|
data, err := json.MarshalIndent(modelsList, "", " ") |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
|
|
|
return ioutil.WriteFile(modelsListFile, data, 0644) |
|
|
} |
|
|
|
|
|
|
|
|
func loadModelsList() { |
|
|
|
|
|
if _, err := os.Stat(modelsListFile); os.IsNotExist(err) { |
|
|
initializeModelsFromDefaults() |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
data, err := ioutil.ReadFile(modelsListFile) |
|
|
if err != nil { |
|
|
logger.SysLog(fmt.Sprintf("无法读取模型列表文件: %s", err.Error())) |
|
|
initializeModelsFromDefaults() |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &modelsList); err != nil { |
|
|
logger.SysLog(fmt.Sprintf("解析模型列表文件失败: %s", err.Error())) |
|
|
initializeModelsFromDefaults() |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
if len(modelsList) == 0 { |
|
|
initializeModelsFromDefaults() |
|
|
} |
|
|
|
|
|
|
|
|
updateDefaultOpenaiModelList() |
|
|
} |
|
|
|
|
|
|
|
|
func updateDefaultOpenaiModelList() { |
|
|
|
|
|
common.DefaultOpenaiModelList = []string{} |
|
|
|
|
|
|
|
|
for _, model := range modelsList { |
|
|
if model.Enabled { |
|
|
common.DefaultOpenaiModelList = append(common.DefaultOpenaiModelList, model.ID) |
|
|
} |
|
|
} |
|
|
} |
|
|
|