mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-26 13:04:30 +08:00
插件配置已完成
This commit is contained in:
@@ -25,55 +25,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthConfig 定义授权配置
|
|
||||||
type AuthConfig struct {
|
|
||||||
ExactPaths map[string]bool // 精确匹配的路径
|
|
||||||
PrefixPaths map[string]bool // 前缀匹配的路径
|
|
||||||
}
|
|
||||||
|
|
||||||
var authConfig = &AuthConfig{
|
|
||||||
ExactPaths: map[string]bool{
|
|
||||||
"/api/user/login": false,
|
|
||||||
"/api/user/logout": false,
|
|
||||||
"/api/user/resetPass": false,
|
|
||||||
"/api/user/register": false,
|
|
||||||
"/api/user/clogin": false,
|
|
||||||
"/api/user/clogin/callback": false,
|
|
||||||
"/api/user/signin": false,
|
|
||||||
"/api/admin/login": false,
|
|
||||||
"/api/admin/logout": false,
|
|
||||||
"/api/admin/login/captcha": false,
|
|
||||||
"/api/app/list": false,
|
|
||||||
"/api/app/type/list": false,
|
|
||||||
"/api/app/list/user": false,
|
|
||||||
"/api/model/list": false,
|
|
||||||
"/api/mj/imgWall": false,
|
|
||||||
"/api/mj/notify": false,
|
|
||||||
"/api/invite/hits": false,
|
|
||||||
"/api/sd/imgWall": false,
|
|
||||||
"/api/dall/imgWall": false,
|
|
||||||
"/api/product/list": false,
|
|
||||||
"/api/menu/list": false,
|
|
||||||
"/api/markMap/client": false,
|
|
||||||
"/api/payment/doPay": false,
|
|
||||||
"/api/payment/payWays": false,
|
|
||||||
"/api/download": false,
|
|
||||||
"/api/dall/models": false,
|
|
||||||
"/api/chat/message": false, // 聊天接口需要特殊处理
|
|
||||||
"/api/realtime": false, // 实时通信接口需要特殊处理
|
|
||||||
"/api/realtime/voice": false, // 语音聊天接口需要特殊处理
|
|
||||||
},
|
|
||||||
PrefixPaths: map[string]bool{
|
|
||||||
"/api/test/": false,
|
|
||||||
"/api/payment/notify/": false,
|
|
||||||
"/api/config/": false,
|
|
||||||
"/api/function/": false,
|
|
||||||
"/api/sms/": false,
|
|
||||||
"/api/captcha/": false,
|
|
||||||
"/static/": false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppServer struct {
|
type AppServer struct {
|
||||||
Config *types.AppConfig
|
Config *types.AppConfig
|
||||||
Engine *gin.Engine
|
Engine *gin.Engine
|
||||||
@@ -94,10 +45,10 @@ func NewServer(appConfig *types.AppConfig, redis *redis.Client, sysConfig *types
|
|||||||
|
|
||||||
func (s *AppServer) Init(client *redis.Client) {
|
func (s *AppServer) Init(client *redis.Client) {
|
||||||
s.Engine.Use(middleware.ParameterHandlerMiddleware())
|
s.Engine.Use(middleware.ParameterHandlerMiddleware())
|
||||||
s.Engine.Use(middleware.StaticMiddleware())
|
|
||||||
s.Engine.Use(errorHandler)
|
s.Engine.Use(errorHandler)
|
||||||
// 添加静态资源访问
|
// 添加静态资源访问
|
||||||
s.Engine.Static("/static", s.Config.StaticDir)
|
s.Engine.Static("/static", s.Config.StaticDir)
|
||||||
|
s.Engine.Use(middleware.StaticMiddleware())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AppServer) Run(db *gorm.DB) error {
|
func (s *AppServer) Run(db *gorm.DB) error {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ type MiniOssConfig struct {
|
|||||||
AccessKey string `json:"access_key"`
|
AccessKey string `json:"access_key"`
|
||||||
AccessSecret string `json:"access_secret"`
|
AccessSecret string `json:"access_secret"`
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
SubDir string `json:"sub_dir"`
|
|
||||||
UseSSL bool `json:"use_ssl"`
|
UseSSL bool `json:"use_ssl"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
}
|
}
|
||||||
@@ -30,7 +29,6 @@ type QiNiuOssConfig struct {
|
|||||||
AccessKey string `json:"access_key"`
|
AccessKey string `json:"access_key"`
|
||||||
AccessSecret string `json:"access_secret"`
|
AccessSecret string `json:"access_secret"`
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
SubDir string `json:"sub_dir"`
|
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ package types
|
|||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
type SMSConfig struct {
|
type SMSConfig struct {
|
||||||
Active string
|
Active string `json:"active"`
|
||||||
Ali SmsConfigAli
|
Ali SmsConfigAli `json:"aliyun"`
|
||||||
Bao SmsConfigBao
|
Bao SmsConfigBao `json:"bao"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SmsConfigAli 阿里云短信平台配置
|
// SmsConfigAli 阿里云短信平台配置
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/middleware"
|
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/service"
|
"geekai/service"
|
||||||
"geekai/service/dalle"
|
"geekai/service/dalle"
|
||||||
@@ -60,12 +59,9 @@ func (h *FunctionHandler) RegisterRoutes() {
|
|||||||
group.GET("list", h.List)
|
group.GET("list", h.List)
|
||||||
|
|
||||||
// 需要用户授权的接口
|
// 需要用户授权的接口
|
||||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
group.POST("weibo", h.WeiBo)
|
||||||
{
|
group.POST("zaobao", h.ZaoBao)
|
||||||
group.POST("weibo", h.WeiBo)
|
group.POST("dalle3", h.Dall3)
|
||||||
group.POST("zaobao", h.ZaoBao)
|
|
||||||
group.POST("dalle3", h.Dall3)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type resVo struct {
|
type resVo struct {
|
||||||
|
|||||||
@@ -1,333 +0,0 @@
|
|||||||
package crawler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"geekai/logger"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-rod/rod"
|
|
||||||
"github.com/go-rod/rod/lib/launcher"
|
|
||||||
"github.com/go-rod/rod/lib/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Service 网络爬虫服务
|
|
||||||
type Service struct {
|
|
||||||
browser *rod.Browser
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewService 创建一个新的爬虫服务
|
|
||||||
func NewService() (*Service, error) {
|
|
||||||
// 启动浏览器
|
|
||||||
path, _ := launcher.LookPath()
|
|
||||||
u := launcher.New().Bin(path).
|
|
||||||
Headless(true). // 无头模式
|
|
||||||
Set("disable-web-security", ""). // 禁用网络安全限制
|
|
||||||
Set("disable-gpu", ""). // 禁用 GPU 加速
|
|
||||||
Set("no-sandbox", ""). // 禁用沙箱模式
|
|
||||||
Set("disable-setuid-sandbox", ""). // 禁用 setuid 沙箱
|
|
||||||
MustLaunch()
|
|
||||||
|
|
||||||
browser := rod.New().ControlURL(u).MustConnect()
|
|
||||||
|
|
||||||
return &Service{
|
|
||||||
browser: browser,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchResult 搜索结果
|
|
||||||
type SearchResult struct {
|
|
||||||
Title string `json:"title"` // 标题
|
|
||||||
URL string `json:"url"` // 链接
|
|
||||||
Content string `json:"content"` // 内容摘要
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSearch 网络搜索
|
|
||||||
func (s *Service) WebSearch(keyword string, maxPages int) ([]SearchResult, error) {
|
|
||||||
if keyword == "" {
|
|
||||||
return nil, errors.New("搜索关键词不能为空")
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxPages <= 0 {
|
|
||||||
maxPages = 1
|
|
||||||
}
|
|
||||||
if maxPages > 10 {
|
|
||||||
maxPages = 10 // 最多搜索 10 页
|
|
||||||
}
|
|
||||||
|
|
||||||
results := make([]SearchResult, 0)
|
|
||||||
|
|
||||||
// 使用百度搜索
|
|
||||||
searchURL := fmt.Sprintf("https://www.baidu.com/s?wd=%s", url.QueryEscape(keyword))
|
|
||||||
|
|
||||||
// 设置页面超时
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// 创建页面
|
|
||||||
page := s.browser.MustPage()
|
|
||||||
defer page.MustClose()
|
|
||||||
|
|
||||||
// 设置视口大小
|
|
||||||
err := page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
|
|
||||||
Width: 1280,
|
|
||||||
Height: 800,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("设置视口失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导航到搜索页面
|
|
||||||
err = page.Context(ctx).Navigate(searchURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("导航到搜索页面失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待搜索结果加载完成
|
|
||||||
err = page.WaitLoad()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("等待页面加载完成失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分析当前页面的搜索结果
|
|
||||||
for i := 0; i < maxPages; i++ {
|
|
||||||
if i > 0 {
|
|
||||||
// 点击下一页按钮
|
|
||||||
nextPage, err := page.Element("a.n")
|
|
||||||
if err != nil || nextPage == nil {
|
|
||||||
break // 没有下一页
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nextPage.Click(proto.InputMouseButtonLeft, 1)
|
|
||||||
if err != nil {
|
|
||||||
break // 点击下一页失败
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待新页面加载
|
|
||||||
err = page.WaitLoad()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取搜索结果
|
|
||||||
resultElements, err := page.Elements(".result, .c-container")
|
|
||||||
if err != nil || resultElements == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, result := range resultElements {
|
|
||||||
// 获取标题
|
|
||||||
titleElement, err := result.Element("h3, .t")
|
|
||||||
if err != nil || titleElement == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
title, err := titleElement.Text()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 URL
|
|
||||||
linkElement, err := titleElement.Element("a")
|
|
||||||
if err != nil || linkElement == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
href, err := linkElement.Attribute("href")
|
|
||||||
if err != nil || href == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取内容摘要 - 尝试多个可能的选择器
|
|
||||||
var contentElement *rod.Element
|
|
||||||
var content string
|
|
||||||
|
|
||||||
// 尝试多个可能的选择器来适应不同版本的百度搜索结果
|
|
||||||
selectors := []string{".content-right_8Zs40", ".c-abstract", ".content_LJ0WN", ".content"}
|
|
||||||
for _, selector := range selectors {
|
|
||||||
contentElement, err = result.Element(selector)
|
|
||||||
if err == nil && contentElement != nil {
|
|
||||||
content, _ = contentElement.Text()
|
|
||||||
if content != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果所有选择器都失败,尝试直接从结果块中提取文本
|
|
||||||
if content == "" {
|
|
||||||
// 获取结果元素的所有文本
|
|
||||||
fullText, err := result.Text()
|
|
||||||
if err == nil && fullText != "" {
|
|
||||||
// 简单处理:从全文中移除标题,剩下的可能是摘要
|
|
||||||
fullText = strings.Replace(fullText, title, "", 1)
|
|
||||||
// 清理文本
|
|
||||||
content = strings.TrimSpace(fullText)
|
|
||||||
// 限制内容长度
|
|
||||||
if len(content) > 200 {
|
|
||||||
content = content[:200] + "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加到结果集
|
|
||||||
results = append(results, SearchResult{
|
|
||||||
Title: title,
|
|
||||||
URL: *href,
|
|
||||||
Content: content,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 限制结果数量,每页最多 10 条
|
|
||||||
if len(results) >= 10*maxPages {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取真实 URL(百度搜索结果中的 URL 是短链接,需要跳转获取真实 URL)
|
|
||||||
for i, result := range results {
|
|
||||||
realURL, err := s.getRedirectURL(result.URL)
|
|
||||||
if err == nil && realURL != "" {
|
|
||||||
results[i].URL = realURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取真实 URL
|
|
||||||
func (s *Service) getRedirectURL(shortURL string) (string, error) {
|
|
||||||
// 创建页面
|
|
||||||
page, err := s.browser.Page(proto.TargetCreateTarget{URL: ""})
|
|
||||||
if err != nil {
|
|
||||||
return shortURL, err // 返回原始URL
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = page.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 导航到短链接
|
|
||||||
err = page.Navigate(shortURL)
|
|
||||||
if err != nil {
|
|
||||||
return shortURL, err // 返回原始URL
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待重定向完成
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
// 获取当前 URL
|
|
||||||
info, err := page.Info()
|
|
||||||
if err != nil {
|
|
||||||
return shortURL, err // 返回原始URL
|
|
||||||
}
|
|
||||||
|
|
||||||
return info.URL, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close 关闭浏览器
|
|
||||||
func (s *Service) Close() error {
|
|
||||||
if s.browser != nil {
|
|
||||||
err := s.browser.Close()
|
|
||||||
s.browser = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchWeb 封装的搜索方法
|
|
||||||
func SearchWeb(keyword string, maxPages int) (string, error) {
|
|
||||||
// 添加panic恢复机制
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
log := logger.GetLogger()
|
|
||||||
log.Errorf("爬虫服务崩溃: %v", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
service, err := NewService()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("创建爬虫服务失败: %v", err)
|
|
||||||
}
|
|
||||||
defer service.Close()
|
|
||||||
|
|
||||||
// 设置超时上下文
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// 使用goroutine和通道来处理超时
|
|
||||||
resultChan := make(chan []SearchResult, 1)
|
|
||||||
errChan := make(chan error, 1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
results, err := service.WebSearch(keyword, maxPages)
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultChan <- results
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 等待结果或超时
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return "", fmt.Errorf("搜索超时: %v", ctx.Err())
|
|
||||||
case err := <-errChan:
|
|
||||||
return "", fmt.Errorf("搜索失败: %v", err)
|
|
||||||
case results := <-resultChan:
|
|
||||||
if len(results) == 0 {
|
|
||||||
return "未找到关于 \"" + keyword + "\" 的相关搜索结果", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化结果
|
|
||||||
var builder strings.Builder
|
|
||||||
builder.WriteString(fmt.Sprintf("为您找到关于 \"%s\" 的 %d 条搜索结果:\n\n", keyword, len(results)))
|
|
||||||
|
|
||||||
for i, result := range results {
|
|
||||||
// // 尝试打开链接获取实际内容
|
|
||||||
// page := service.browser.MustPage()
|
|
||||||
// defer page.MustClose()
|
|
||||||
|
|
||||||
// // 设置页面超时
|
|
||||||
// pageCtx, pageCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
// defer pageCancel()
|
|
||||||
|
|
||||||
// // 导航到目标页面
|
|
||||||
// err := page.Context(pageCtx).Navigate(result.URL)
|
|
||||||
// if err == nil {
|
|
||||||
// // 等待页面加载
|
|
||||||
// _ = page.WaitLoad()
|
|
||||||
|
|
||||||
// // 获取页面标题
|
|
||||||
// title, err := page.Eval("() => document.title")
|
|
||||||
// if err == nil && title.Value.String() != "" {
|
|
||||||
// result.Title = title.Value.String()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 获取页面主要内容
|
|
||||||
// if content, err := page.Element("body"); err == nil {
|
|
||||||
// if text, err := content.Text(); err == nil {
|
|
||||||
// // 清理并截取内容
|
|
||||||
// text = strings.TrimSpace(text)
|
|
||||||
// if len(text) > 200 {
|
|
||||||
// text = text[:200] + "..."
|
|
||||||
// }
|
|
||||||
// result.Prompt = text
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
builder.WriteString(fmt.Sprintf("%d. **%s**\n", i+1, result.Title))
|
|
||||||
builder.WriteString(fmt.Sprintf(" 链接: %s\n", result.URL))
|
|
||||||
if result.Content != "" {
|
|
||||||
builder.WriteString(fmt.Sprintf(" 摘要: %s\n", result.Content))
|
|
||||||
}
|
|
||||||
builder.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.String(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -122,15 +122,6 @@ type ErrRes struct {
|
|||||||
|
|
||||||
func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
||||||
logger.Debugf("绘画参数:%+v", task)
|
logger.Debugf("绘画参数:%+v", task)
|
||||||
prompt := task.Prompt
|
|
||||||
// translate prompt
|
|
||||||
if utils.HasChinese(prompt) {
|
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, prompt), task.TranslateModelId)
|
|
||||||
if err == nil {
|
|
||||||
prompt = content
|
|
||||||
logger.Debugf("重写后提示词:%s", prompt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var chatModel model.ChatModel
|
var chatModel model.ChatModel
|
||||||
if task.ModelId > 0 {
|
if task.ModelId > 0 {
|
||||||
@@ -160,7 +151,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
|||||||
apiURL := fmt.Sprintf("%s/v1/images/generations", apiKey.ApiURL)
|
apiURL := fmt.Sprintf("%s/v1/images/generations", apiKey.ApiURL)
|
||||||
reqBody := imgReq{
|
reqBody := imgReq{
|
||||||
Model: chatModel.Value,
|
Model: chatModel.Value,
|
||||||
Prompt: prompt,
|
Prompt: task.Prompt,
|
||||||
N: 1,
|
N: 1,
|
||||||
Size: task.Size,
|
Size: task.Size,
|
||||||
Style: task.Style,
|
Style: task.Style,
|
||||||
@@ -188,7 +179,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
|||||||
var imgURL string
|
var imgURL string
|
||||||
var data = map[string]interface{}{
|
var data = map[string]interface{}{
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"prompt": prompt,
|
"prompt": task.Prompt,
|
||||||
}
|
}
|
||||||
// 如果返回的是base64,则需要上传到oss
|
// 如果返回的是base64,则需要上传到oss
|
||||||
if res.Data[0].B64Json != "" {
|
if res.Data[0].B64Json != "" {
|
||||||
@@ -214,7 +205,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error with download image: %v", err)
|
return "", fmt.Errorf("error with download image: %v", err)
|
||||||
}
|
}
|
||||||
content = fmt.Sprintf("```\n%s\n```\n下面是我为你创作的图片:\n\n\n", prompt, imgURL)
|
content = fmt.Sprintf("```\n%s\n```\n下面是我为你创作的图片:\n\n\n", task.Prompt, imgURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
return content, nil
|
return content, nil
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/store"
|
"geekai/store"
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -191,8 +192,8 @@ func (s *MigrationService) migrateCommunicationConfig(config *types.AppConfig) e
|
|||||||
|
|
||||||
// 短信配置
|
// 短信配置
|
||||||
smsConfig := map[string]any{
|
smsConfig := map[string]any{
|
||||||
"active": config.SMS.Active,
|
"active": strings.ToLower(config.SMS.Active),
|
||||||
"ali": map[string]any{
|
"aliyun": map[string]any{
|
||||||
"access_key": config.SMS.Ali.AccessKey,
|
"access_key": config.SMS.Ali.AccessKey,
|
||||||
"access_secret": config.SMS.Ali.AccessSecret,
|
"access_secret": config.SMS.Ali.AccessSecret,
|
||||||
"sign": config.SMS.Ali.Sign,
|
"sign": config.SMS.Ali.Sign,
|
||||||
|
|||||||
@@ -67,25 +67,6 @@ func (s *Service) Run() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate prompt
|
|
||||||
if utils.HasChinese(task.Prompt) {
|
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), task.TranslateModelId)
|
|
||||||
if err == nil {
|
|
||||||
task.Prompt = content
|
|
||||||
} else {
|
|
||||||
logger.Warnf("error with translate prompt: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// translate negative prompt
|
|
||||||
if task.NegPrompt != "" && utils.HasChinese(task.NegPrompt) {
|
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.NegPrompt), task.TranslateModelId)
|
|
||||||
if err == nil {
|
|
||||||
task.NegPrompt = content
|
|
||||||
} else {
|
|
||||||
logger.Warnf("error with translate prompt: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use fast mode as default
|
// use fast mode as default
|
||||||
if task.Mode == "" {
|
if task.Mode == "" {
|
||||||
task.Mode = "fast"
|
task.Mode = "fast"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (s MiniOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string,
|
|||||||
if ext == "" {
|
if ext == "" {
|
||||||
ext = filepath.Ext(parse.Path)
|
ext = filepath.Ext(parse.Path)
|
||||||
}
|
}
|
||||||
filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext)
|
filename := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext)
|
||||||
info, err := s.client.PutObject(
|
info, err := s.client.PutObject(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
s.config.Bucket,
|
s.config.Bucket,
|
||||||
@@ -99,7 +99,7 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
|||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
|
|
||||||
fileExt := filepath.Ext(file.Filename)
|
fileExt := filepath.Ext(file.Filename)
|
||||||
filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
filename := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt)
|
||||||
info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{
|
info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{
|
||||||
ContentType: file.Header.Get("Body-Type"),
|
ContentType: file.Header.Get("Body-Type"),
|
||||||
})
|
})
|
||||||
@@ -121,7 +121,7 @@ func (s MiniOss) PutBase64(base64Img string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error decoding base64:%v", err)
|
return "", fmt.Errorf("error decoding base64:%v", err)
|
||||||
}
|
}
|
||||||
objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro())
|
objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro())
|
||||||
info, err := s.client.PutObject(
|
info, err := s.client.PutObject(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
s.config.Bucket,
|
s.config.Bucket,
|
||||||
@@ -138,8 +138,7 @@ func (s MiniOss) PutBase64(base64Img string) (string, error) {
|
|||||||
func (s MiniOss) Delete(fileURL string) error {
|
func (s MiniOss) Delete(fileURL string) error {
|
||||||
var objectKey string
|
var objectKey string
|
||||||
if strings.HasPrefix(fileURL, "http") {
|
if strings.HasPrefix(fileURL, "http") {
|
||||||
filename := filepath.Base(fileURL)
|
objectKey = filepath.Base(fileURL)
|
||||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
|
||||||
} else {
|
} else {
|
||||||
objectKey = fileURL
|
objectKey = fileURL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (s QiNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
|||||||
defer src.Close()
|
defer src.Close()
|
||||||
|
|
||||||
fileExt := filepath.Ext(file.Filename)
|
fileExt := filepath.Ext(file.Filename)
|
||||||
key := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
key := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt)
|
||||||
// 上传文件
|
// 上传文件
|
||||||
ret := storage.PutRet{}
|
ret := storage.PutRet{}
|
||||||
extra := storage.PutExtra{}
|
extra := storage.PutExtra{}
|
||||||
@@ -112,7 +112,7 @@ func (s QiNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string,
|
|||||||
if ext == "" {
|
if ext == "" {
|
||||||
ext = filepath.Ext(parse.Path)
|
ext = filepath.Ext(parse.Path)
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext)
|
key := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext)
|
||||||
ret := storage.PutRet{}
|
ret := storage.PutRet{}
|
||||||
extra := storage.PutExtra{}
|
extra := storage.PutExtra{}
|
||||||
// 上传文件字节数据
|
// 上传文件字节数据
|
||||||
@@ -128,7 +128,7 @@ func (s QiNiuOss) PutBase64(base64Img string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error decoding base64:%v", err)
|
return "", fmt.Errorf("error decoding base64:%v", err)
|
||||||
}
|
}
|
||||||
objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro())
|
objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro())
|
||||||
ret := storage.PutRet{}
|
ret := storage.PutRet{}
|
||||||
extra := storage.PutExtra{}
|
extra := storage.PutExtra{}
|
||||||
// 上传文件字节数据
|
// 上传文件字节数据
|
||||||
@@ -142,8 +142,7 @@ func (s QiNiuOss) PutBase64(base64Img string) (string, error) {
|
|||||||
func (s QiNiuOss) Delete(fileURL string) error {
|
func (s QiNiuOss) Delete(fileURL string) error {
|
||||||
var objectKey string
|
var objectKey string
|
||||||
if strings.HasPrefix(fileURL, "http") {
|
if strings.HasPrefix(fileURL, "http") {
|
||||||
filename := filepath.Base(fileURL)
|
objectKey = filepath.Base(fileURL)
|
||||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
|
||||||
} else {
|
} else {
|
||||||
objectKey = fileURL
|
objectKey = fileURL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ package resp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SUCCESS(c *gin.Context, values ...interface{}) {
|
func SUCCESS(c *gin.Context, values ...interface{}) {
|
||||||
|
|||||||
@@ -223,14 +223,19 @@ const items = [
|
|||||||
title: '存储配置',
|
title: '存储配置',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'log',
|
icon: 'sms',
|
||||||
index: '/admin/config/communication',
|
index: '/admin/config/sms',
|
||||||
title: '通信配置',
|
title: '短信配置',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'api-key',
|
icon: 'email',
|
||||||
index: '/admin/config/api',
|
index: '/admin/config/smtp',
|
||||||
title: 'API配置',
|
title: '邮件配置',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'plugin',
|
||||||
|
index: '/admin/config/plugin',
|
||||||
|
title: '插件配置',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
47
web/src/components/ui/Alert.vue
Normal file
47
web/src/components/ui/Alert.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div class="rounded-md p-3 border-2 text-base mb-4 flex items-center" :class="typeClass">
|
||||||
|
<div :class="textColor">
|
||||||
|
<slot name="icon">
|
||||||
|
<i class="iconfont !text-xl" :class="typeIcon"></i>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm leading-relaxed">
|
||||||
|
<slot>{{ message }}</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'info',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const typeClass = computed(() => {
|
||||||
|
return {
|
||||||
|
info: 'bg-blue-100 text-blue-500 border-blue-500',
|
||||||
|
success: 'bg-green-100 text-green-500 border-green-500',
|
||||||
|
warning: 'bg-yellow-100 text-yellow-500 border-yellow-500',
|
||||||
|
}[props.type]
|
||||||
|
})
|
||||||
|
const typeIcon = computed(() => {
|
||||||
|
return {
|
||||||
|
info: 'icon-info',
|
||||||
|
success: 'icon-success',
|
||||||
|
warning: 'icon-warning',
|
||||||
|
}[props.type]
|
||||||
|
})
|
||||||
|
const textColor = computed(() => {
|
||||||
|
return {
|
||||||
|
info: 'text-blue-500',
|
||||||
|
success: 'text-green-500',
|
||||||
|
warning: 'text-yellow-500',
|
||||||
|
}[props.type]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -192,16 +192,22 @@ const routes = [
|
|||||||
component: () => import('@/views/admin/settings/StorageConfig.vue'),
|
component: () => import('@/views/admin/settings/StorageConfig.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/config/communication',
|
path: '/admin/config/sms',
|
||||||
name: 'admin-config-communication',
|
name: 'admin-config-sms',
|
||||||
meta: { title: '通信配置' },
|
meta: { title: '短信配置' },
|
||||||
component: () => import('@/views/admin/settings/CommunicationConfig.vue'),
|
component: () => import('@/views/admin/settings/SmsConfig.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/config/api',
|
path: '/admin/config/smtp',
|
||||||
name: 'admin-config-api',
|
name: 'admin-config-smtp',
|
||||||
meta: { title: 'API配置' },
|
meta: { title: '邮件配置' },
|
||||||
component: () => import('@/views/admin/settings/ApiConfig.vue'),
|
component: () => import('@/views/admin/settings/SmtpConfig.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/config/plugin',
|
||||||
|
name: 'admin-config-plugin',
|
||||||
|
meta: { title: '插件配置' },
|
||||||
|
component: () => import('@/views/admin/settings/PluginConfig.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/config/markmap',
|
path: '/admin/config/markmap',
|
||||||
|
|||||||
@@ -5,8 +5,32 @@
|
|||||||
// * @Author yangjian102621@163.com
|
// * @Author yangjian102621@163.com
|
||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
import axios from 'axios'
|
|
||||||
import { getAdminToken, getUserToken, removeAdminToken, removeUserToken } from '@/store/session'
|
import { getAdminToken, getUserToken, removeAdminToken, removeUserToken } from '@/store/session'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
// Blob 数据读取和解析的辅助函数
|
||||||
|
export async function parseBlobResponse(blob) {
|
||||||
|
try {
|
||||||
|
// 检查 Blob 的类型
|
||||||
|
if (blob.type && blob.type.includes('application/json')) {
|
||||||
|
// 如果是 JSON 类型,直接解析
|
||||||
|
const text = await blob.text()
|
||||||
|
return JSON.parse(text)
|
||||||
|
} else {
|
||||||
|
// 如果不是 JSON 类型,尝试解析为文本
|
||||||
|
const text = await blob.text()
|
||||||
|
try {
|
||||||
|
return JSON.parse(text)
|
||||||
|
} catch (e) {
|
||||||
|
// 如果解析 JSON 失败,返回文本内容
|
||||||
|
return { message: text, rawData: text }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析 Blob 响应失败:', error)
|
||||||
|
return { message: '解析响应数据失败', error: error.message }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
axios.defaults.timeout = 180000
|
axios.defaults.timeout = 180000
|
||||||
// axios.defaults.baseURL = process.env.VUE_APP_API_HOST
|
// axios.defaults.baseURL = process.env.VUE_APP_API_HOST
|
||||||
@@ -29,7 +53,7 @@ axios.interceptors.response.use(
|
|||||||
(response) => {
|
(response) => {
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
(error) => {
|
async (error) => {
|
||||||
if (error.response.status === 401) {
|
if (error.response.status === 401) {
|
||||||
if (error.response.request.responseURL.indexOf('/api/admin') !== -1) {
|
if (error.response.request.responseURL.indexOf('/api/admin') !== -1) {
|
||||||
removeAdminToken()
|
removeAdminToken()
|
||||||
@@ -39,8 +63,14 @@ axios.interceptors.response.use(
|
|||||||
error.response.data.message = '请先登录'
|
error.response.data.message = '请先登录'
|
||||||
return Promise.reject(error.response.data)
|
return Promise.reject(error.response.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.response.status === 400) {
|
if (error.response.status === 400) {
|
||||||
return Promise.reject(new Error(error.response.data.message))
|
let errorMessage = error.response.data.message
|
||||||
|
if (!errorMessage) {
|
||||||
|
const parsedData = await parseBlobResponse(error.response.data)
|
||||||
|
errorMessage = parsedData.message
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(errorMessage))
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="reset-pass"></div>
|
|
||||||
<div class="flex-center loginPage">
|
<div class="flex-center loginPage">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="form" v-loading="loading">
|
|
||||||
<el-tabs v-model="active">
|
|
||||||
<el-tab-pane label="SMTP 邮件" name="smtp">
|
|
||||||
<el-form :model="smtp" label-width="140px">
|
|
||||||
<el-form-item label="启用TLS"><el-switch v-model="smtp.use_tls" /></el-form-item>
|
|
||||||
<el-form-item label="SMTP服务器"><el-input v-model="smtp.host" /></el-form-item>
|
|
||||||
<el-form-item label="端口"><el-input-number v-model="smtp.port" :min="1" /></el-form-item>
|
|
||||||
<el-form-item label="应用名称"><el-input v-model="smtp.app_name" /></el-form-item>
|
|
||||||
<el-form-item label="发件人地址"><el-input v-model="smtp.from" /></el-form-item>
|
|
||||||
<el-form-item label="发件人密码"
|
|
||||||
><el-input v-model="smtp.password" type="password"
|
|
||||||
/></el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" @click="save('smtp')">保存</el-button>
|
|
||||||
<el-button @click="test('smtp')">测试</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="短信服务" name="sms">
|
|
||||||
<el-form :model="sms" label-width="140px">
|
|
||||||
<el-form-item label="服务商">
|
|
||||||
<el-select v-model="sms.Active" style="width: 200px">
|
|
||||||
<el-option label="阿里云" value="Ali" />
|
|
||||||
<el-option label="短信宝" value="Bao" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<template v-if="sms.Active === 'Ali'">
|
|
||||||
<el-form-item label="AccessKey"><el-input v-model="sms.Ali.AccessKey" /></el-form-item>
|
|
||||||
<el-form-item label="AccessSecret"
|
|
||||||
><el-input v-model="sms.Ali.AccessSecret"
|
|
||||||
/></el-form-item>
|
|
||||||
<el-form-item label="签名"><el-input v-model="sms.Ali.Sign" /></el-form-item>
|
|
||||||
<el-form-item label="模板ID"><el-input v-model="sms.Ali.CodeTempId" /></el-form-item>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<el-form-item label="用户名"><el-input v-model="sms.Bao.Username" /></el-form-item>
|
|
||||||
<el-form-item label="密码"
|
|
||||||
><el-input v-model="sms.Bao.Password" type="password"
|
|
||||||
/></el-form-item>
|
|
||||||
<el-form-item label="签名"><el-input v-model="sms.Bao.Sign" /></el-form-item>
|
|
||||||
<el-form-item label="模板"
|
|
||||||
><el-input v-model="sms.Bao.CodeTemplate" type="textarea" :rows="2"
|
|
||||||
/></el-form-item>
|
|
||||||
</template>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" @click="save('sms')">保存</el-button>
|
|
||||||
<el-button @click="test('sms')">测试</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { httpGet, httpPost } from '@/utils/http'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
const loading = ref(true)
|
|
||||||
const active = ref('smtp')
|
|
||||||
const smtp = ref({ use_tls: false, host: '', port: 25, app_name: '', from: '', password: '' })
|
|
||||||
const sms = ref({
|
|
||||||
Active: 'Ali',
|
|
||||||
Ali: { AccessKey: '', AccessSecret: '', Sign: '', CodeTempId: '' },
|
|
||||||
Bao: { Username: '', Password: '', Sign: '', CodeTemplate: '' },
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
Promise.all([httpGet('/api/admin/config/get?key=smtp'), httpGet('/api/admin/config/get?key=sms')])
|
|
||||||
.then(([s1, s2]) => {
|
|
||||||
const smtpData = s1?.data || {}
|
|
||||||
smtp.value = { ...smtp.value, ...smtpData }
|
|
||||||
|
|
||||||
const smsData = s2?.data || {}
|
|
||||||
sms.value = {
|
|
||||||
...sms.value,
|
|
||||||
...smsData,
|
|
||||||
Ali: { ...sms.value.Ali, ...(smsData.Ali || smsData.ali || {}) },
|
|
||||||
Bao: { ...sms.value.Bao, ...(smsData.Bao || smsData.bao || {}) },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
.finally(() => (loading.value = false))
|
|
||||||
})
|
|
||||||
|
|
||||||
const save = (key) => {
|
|
||||||
if (key === 'smtp') {
|
|
||||||
httpPost('/api/admin/config/update/smtp', smtp.value)
|
|
||||||
.then(() => ElMessage.success('保存成功'))
|
|
||||||
.catch((e) => ElMessage.error(e.message))
|
|
||||||
} else if (key === 'sms') {
|
|
||||||
httpPost('/api/admin/config/update/sms', sms.value)
|
|
||||||
.then(() => ElMessage.success('保存成功'))
|
|
||||||
.catch((e) => ElMessage.error(e.message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const test = (key) => {
|
|
||||||
ElMessage.info('请在对应服务侧进行验证')
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.form {
|
|
||||||
padding: 10px 20px 40px 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -8,14 +8,14 @@
|
|||||||
<span class="ms-2">支付宝</span>
|
<span class="ms-2">支付宝</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
|
<Alert type="info">
|
||||||
如果你不知道怎么获取这些配置信息,请参考文档:
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
<a
|
<a
|
||||||
href="https://docs.geekai.me/plus/config/payment.html#%E6%94%AF%E4%BB%98%E5%AE%9D%E9%85%8D%E7%BD%AE"
|
href="https://docs.geekai.me/plus/config/payment.html#%E6%94%AF%E4%BB%98%E5%AE%9D%E9%85%8D%E7%BD%AE"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>支付宝配置</a
|
>支付宝配置</a
|
||||||
>。
|
>。
|
||||||
</div>
|
</Alert>
|
||||||
|
|
||||||
<el-form :model="alipay" class="mt-4" label-position="top">
|
<el-form :model="alipay" class="mt-4" label-position="top">
|
||||||
<el-form-item label="商户ID"><el-input v-model="alipay.app_id" /></el-form-item>
|
<el-form-item label="商户ID"><el-input v-model="alipay.app_id" /></el-form-item>
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
|
<Alert type="info">
|
||||||
如果你不知道怎么获取这些配置信息,请参考文档:
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
<a
|
<a
|
||||||
href="https://docs.geekai.me/plus/config/payment.html#%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E9%85%8D%E7%BD%AE"
|
href="https://docs.geekai.me/plus/config/payment.html#%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E9%85%8D%E7%BD%AE"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>微信支付配置</a
|
>微信支付配置</a
|
||||||
>。
|
>。
|
||||||
</div>
|
</Alert>
|
||||||
|
|
||||||
<el-form :model="wxpay" class="mt-4" label-position="top">
|
<el-form :model="wxpay" class="mt-4" label-position="top">
|
||||||
<el-form-item label="AppID"><el-input v-model="wxpay.app_id" /></el-form-item>
|
<el-form-item label="AppID"><el-input v-model="wxpay.app_id" /></el-form-item>
|
||||||
@@ -89,14 +89,14 @@
|
|||||||
<span class="ms-2">易支付</span>
|
<span class="ms-2">易支付</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
|
<Alert type="info">
|
||||||
如果你不知道怎么获取这些配置信息,请参考文档:
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
<a
|
<a
|
||||||
href="https://docs.geekai.me/plus/config/payment.html#%E6%98%93%E6%94%AF%E4%BB%98%E5%BC%80%E9%80%9A"
|
href="https://docs.geekai.me/plus/config/payment.html#%E6%98%93%E6%94%AF%E4%BB%98%E5%BC%80%E9%80%9A"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>易支付配置</a
|
>易支付配置</a
|
||||||
>。
|
>。
|
||||||
</div>
|
</Alert>
|
||||||
|
|
||||||
<el-form :model="epay" class="mt-4" label-position="top">
|
<el-form :model="epay" class="mt-4" label-position="top">
|
||||||
<el-form-item label="商户ID"><el-input v-model="epay.app_id" /></el-form-item>
|
<el-form-item label="商户ID"><el-input v-model="epay.app_id" /></el-form-item>
|
||||||
@@ -114,6 +114,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import Alert from '@/components/ui/Alert.vue'
|
||||||
import { httpGet, httpPost } from '@/utils/http'
|
import { httpGet, httpPost } from '@/utils/http'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
|
|||||||
159
web/src/views/admin/settings/PluginConfig.vue
Normal file
159
web/src/views/admin/settings/PluginConfig.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="settings container p-5">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<el-tab-pane name="captcha">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="iconfont icon-yanzm"></i>
|
||||||
|
<span class="ml-2">行为验证码</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Alert type="info">
|
||||||
|
行为验证码服务,开启后用户登录的时候需要进行行为验证,可以有效防止恶意登录。<br />
|
||||||
|
请联系作者获取令牌填入下面输入框开通验证服务。
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<el-form :model="captchaConfig" label-position="top">
|
||||||
|
<el-form-item label="服务令牌">
|
||||||
|
<el-input v-model="captchaConfig.api_key" placeholder="请输入服务令牌" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="验证码类型">
|
||||||
|
<el-radio-group v-model="captchaConfig.type" size="large">
|
||||||
|
<el-radio value="dot" border>点选验证码</el-radio>
|
||||||
|
<el-radio value="slide" border>滑动验证码</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="启用验证码">
|
||||||
|
<el-switch size="large" v-model="captchaConfig.enabled" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="flex justify-center mt-6">
|
||||||
|
<el-button type="primary" @click="saveCaptchaConfig" :loading="loading"
|
||||||
|
>提交保存</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane name="wxlogin">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="iconfont icon-wechat"></i>
|
||||||
|
<span class="ml-2">微信登录</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Alert type="info">
|
||||||
|
微信登录服务,开启后用户可以使用微信扫码登录。<br />
|
||||||
|
请联系作者获取令牌填入下面输入框开通验证服务。
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<el-form :model="wxLoginConfig" label-position="top">
|
||||||
|
<el-form-item label="服务令牌">
|
||||||
|
<el-input v-model="wxLoginConfig.api_key" placeholder="请输入服务令牌" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="登录成功回调URL">
|
||||||
|
<el-input v-model="wxLoginConfig.notify_url" placeholder="请输入回调URL" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="启用微信登录">
|
||||||
|
<el-switch size="large" v-model="wxLoginConfig.enabled" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="flex justify-center mt-6">
|
||||||
|
<el-button type="primary" @click="saveWxloginConfig" :loading="loading"
|
||||||
|
>提交保存</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Alert from '@/components/ui/Alert.vue'
|
||||||
|
import { httpGet, httpPost } from '@/utils/http.js'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const activeTab = ref('captcha')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 行为验证码配置
|
||||||
|
const captchaConfig = ref({
|
||||||
|
api_key: '',
|
||||||
|
type: 'slide',
|
||||||
|
enabled: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 微信登录配置
|
||||||
|
const wxLoginConfig = ref({
|
||||||
|
api_key: '',
|
||||||
|
notify_url: '',
|
||||||
|
enabled: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCaptchaConfig()
|
||||||
|
fetchWxloginConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchCaptchaConfig = async () => {
|
||||||
|
try {
|
||||||
|
const res = await httpGet('/api/admin/config/get?key=captcha')
|
||||||
|
captchaConfig.value = Object.assign(captchaConfig.value, res.data)
|
||||||
|
} catch (e) {
|
||||||
|
// 使用默认值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchWxloginConfig = async () => {
|
||||||
|
try {
|
||||||
|
const res = await httpGet('/api/admin/config/get?key=wx_login')
|
||||||
|
wxLoginConfig.value = Object.assign(wxLoginConfig.value, res.data)
|
||||||
|
wxLoginConfig.value.notify_url =
|
||||||
|
wxLoginConfig.value.notify_url || window.location.origin + '/api/user/login/callback'
|
||||||
|
} catch (e) {
|
||||||
|
// 使用默认值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存行为验证码配置
|
||||||
|
const saveCaptchaConfig = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await httpPost('/api/admin/config/update/captcha', captchaConfig.value)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('保存失败:' + (e.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存微信登录配置
|
||||||
|
const saveWxloginConfig = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await httpPost('/api/admin/config/update/wx_login', wxLoginConfig.value)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('保存失败:' + (e.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.settings {
|
||||||
|
.el-form-item__label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
143
web/src/views/admin/settings/SmsConfig.vue
Normal file
143
web/src/views/admin/settings/SmsConfig.vue
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<div class="settings container p-5">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<el-tab-pane label="阿里云短信" name="aliyun">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="iconfont icon-aliyun"></i>
|
||||||
|
<span class="ml-2">阿里云短信</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Alert type="info">
|
||||||
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
|
<a
|
||||||
|
href="https://docs.geekai.me/plus/config/sms.html#%E9%98%BF%E9%87%8C%E4%BA%91"
|
||||||
|
target="_blank"
|
||||||
|
>阿里云短信配置</a
|
||||||
|
>。
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<el-form :model="configs.aliyun" label-position="top">
|
||||||
|
<el-form-item label="AccessKey">
|
||||||
|
<el-input v-model="configs.aliyun.access_key" placeholder="请输入AccessKey" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="AccessSecret">
|
||||||
|
<el-input v-model="configs.aliyun.access_secret" placeholder="请输入AccessSecret" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="短信签名">
|
||||||
|
<el-input v-model="configs.aliyun.sign" placeholder="请输入短信签名" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码模板ID">
|
||||||
|
<el-input v-model="configs.aliyun.code_temp_id" placeholder="请输入验证码模板ID" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="短信宝" name="bao">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="iconfont icon-sms"></i>
|
||||||
|
<span class="ml-2">短信宝</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Alert type="info">
|
||||||
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
|
<a
|
||||||
|
href="https://docs.geekai.me/plus/config/sms.html#%E7%9F%AD%E4%BF%A1%E5%AE%9D"
|
||||||
|
target="_blank"
|
||||||
|
>短信宝配置</a
|
||||||
|
>。
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<el-form :model="configs.bao" label-position="top">
|
||||||
|
<el-form-item label="用户名">
|
||||||
|
<el-input v-model="configs.bao.username" placeholder="请输入用户名" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="configs.bao.password" placeholder="请输入密码" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="短信签名">
|
||||||
|
<el-input v-model="configs.bao.sign" placeholder="请输入短信签名" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码模板">
|
||||||
|
<el-input v-model="configs.bao.code_template" placeholder="请输入验证码模板" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<label class="form-label mr-2">默认使用</label>
|
||||||
|
<el-radio-group v-model="configs.active" size="large">
|
||||||
|
<el-radio value="aliyun" border>阿里云</el-radio>
|
||||||
|
<el-radio value="bao" border>短信宝</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-center mt-6">
|
||||||
|
<el-button type="primary" @click="saveSmsConfig" :loading="loading">提交保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Alert from '@/components/ui/Alert.vue'
|
||||||
|
import { httpGet, httpPost } from '@/utils/http'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const activeTab = ref('aliyun')
|
||||||
|
const configs = ref({
|
||||||
|
active: 'aliyun',
|
||||||
|
aliyun: {
|
||||||
|
access_key: '',
|
||||||
|
access_secret: '',
|
||||||
|
sign: '',
|
||||||
|
code_temp_id: '',
|
||||||
|
},
|
||||||
|
bao: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
sign: '',
|
||||||
|
code_template: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await httpGet('/api/admin/config/get?key=sms')
|
||||||
|
configs.value = Object.assign(configs.value, res.data)
|
||||||
|
} catch (e) {
|
||||||
|
// 使用默认值
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const saveSmsConfig = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await httpPost('/api/admin/config/update/sms', configs.value)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('保存失败:' + (e.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.settings {
|
||||||
|
a {
|
||||||
|
color: #409eff;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item__label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
140
web/src/views/admin/settings/SmtpConfig.vue
Normal file
140
web/src/views/admin/settings/SmtpConfig.vue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<div class="settings container p-5">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<el-tab-pane label="邮件服务设置" name="smtp">
|
||||||
|
<Alert type="info">
|
||||||
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
|
<a
|
||||||
|
href="https://docs.geekai.me/plus/config/sms.html#%E9%82%AE%E4%BB%B6%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AE"
|
||||||
|
target="_blank"
|
||||||
|
>邮件服务配置</a
|
||||||
|
>。
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<el-form :model="smtpConfig" label-position="top">
|
||||||
|
<el-form-item>
|
||||||
|
<label class="form-label"
|
||||||
|
>邮件服务器地址
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content>
|
||||||
|
推荐使用网易邮箱,<br />
|
||||||
|
国外邮箱推荐使用 Google 邮箱
|
||||||
|
</template>
|
||||||
|
<i class="iconfont icon-info"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</label>
|
||||||
|
<el-input
|
||||||
|
v-model="smtpConfig.host"
|
||||||
|
placeholder="请输入邮件服务器地址,推荐使用网易邮箱"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<label class="form-label"
|
||||||
|
>邮件服务器端口
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content> 线上推荐使用465端口,<br />本地测试推荐使用25端口 </template>
|
||||||
|
<i class="iconfont icon-info"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</label>
|
||||||
|
<el-input v-model="smtpConfig.port" type="number" placeholder="请输入端口号" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="是否使用TLS"
|
||||||
|
><el-switch v-model="smtpConfig.use_tls"
|
||||||
|
/></el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<label class="form-label"
|
||||||
|
>应用名称
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content> 应用名称会显示在邮件的抬头 </template>
|
||||||
|
<i class="iconfont icon-info"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</label>
|
||||||
|
<el-input v-model="smtpConfig.app_name" placeholder="请输入应用名称" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<label class="form-label">发件人邮箱地址</label>
|
||||||
|
<el-input v-model="smtpConfig.from" type="email" placeholder="请输入发件人邮箱地址" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<label class="form-label"
|
||||||
|
>发件人邮箱密码
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content> 如果使用授权码,请输入授权码 </template>
|
||||||
|
<i class="iconfont icon-info"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</label>
|
||||||
|
<el-input
|
||||||
|
v-model="smtpConfig.password"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入邮箱密码或授权码"
|
||||||
|
show-password
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="flex justify-center mt-6">
|
||||||
|
<el-button type="primary" @click="saveConfig" :loading="loading">提交保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Alert from '@/components/ui/Alert.vue'
|
||||||
|
import { httpGet, httpPost } from '@/utils/http'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const activeTab = ref('smtp')
|
||||||
|
const smtpConfig = ref({
|
||||||
|
use_tls: false,
|
||||||
|
host: 'smtp.163.com',
|
||||||
|
port: 465,
|
||||||
|
app_name: 'GeekAI',
|
||||||
|
from: '',
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await httpGet('/api/admin/config/get?key=smtp')
|
||||||
|
smtpConfig.value = Object.assign(smtpConfig.value, res.data || {})
|
||||||
|
} catch (e) {
|
||||||
|
// 使用默认值
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存配置
|
||||||
|
const saveConfig = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await httpPost('/api/admin/config/update/smtp', smtpConfig.value)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('保存失败:' + (e.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.settings {
|
||||||
|
a {
|
||||||
|
color: #409eff;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item__label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -59,14 +59,39 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="七牛云" name="qiniu">
|
<el-tab-pane label="七牛云" name="qiniu">
|
||||||
|
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
|
||||||
|
如果你不知道怎么获取这些配置信息,请参考文档:
|
||||||
|
<a
|
||||||
|
href="https://docs.geekai.me/plus/config/oss.html#%E4%B8%83%E7%89%9B%E4%BA%91-oss-%E9%85%8D%E7%BD%AE"
|
||||||
|
target="_blank"
|
||||||
|
>七牛云配置</a
|
||||||
|
>。
|
||||||
|
</div>
|
||||||
<el-form :model="qiniu" class="mt-4" label-position="top">
|
<el-form :model="qiniu" class="mt-4" label-position="top">
|
||||||
<el-form-item label="Zone"><el-input v-model="qiniu.zone" /></el-form-item>
|
<el-form-item label="Zone">
|
||||||
|
<template #label>
|
||||||
|
<label
|
||||||
|
>区域(Zone)
|
||||||
|
<el-tooltip
|
||||||
|
placement="right"
|
||||||
|
content="华南:z2,华东:z0,华北:z1,北美:na0,新加坡:as0"
|
||||||
|
>
|
||||||
|
<i class="iconfont icon-info"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
<el-input
|
||||||
|
v-model="qiniu.zone"
|
||||||
|
placeholder="华南:z2,华东:z0,华北:z1,北美:na0,新加坡:as0"
|
||||||
|
/></el-form-item>
|
||||||
<el-form-item label="AccessKey"><el-input v-model="qiniu.access_key" /></el-form-item>
|
<el-form-item label="AccessKey"><el-input v-model="qiniu.access_key" /></el-form-item>
|
||||||
<el-form-item label="AccessSecret"
|
<el-form-item label="AccessSecret"
|
||||||
><el-input v-model="qiniu.access_secret"
|
><el-input v-model="qiniu.access_secret"
|
||||||
/></el-form-item>
|
/></el-form-item>
|
||||||
<el-form-item label="Bucket"><el-input v-model="qiniu.bucket" /></el-form-item>
|
<el-form-item label="Bucket"><el-input v-model="qiniu.bucket" /></el-form-item>
|
||||||
<el-form-item label="Domain"><el-input v-model="qiniu.domain" /></el-form-item>
|
<el-form-item label="Domain"
|
||||||
|
><el-input v-model="qiniu.domain" placeholder="请输入七牛云Bucket绑定的域名"
|
||||||
|
/></el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
@@ -95,7 +120,6 @@
|
|||||||
|
|
||||||
<div class="flex justify-center mt-6">
|
<div class="flex justify-center mt-6">
|
||||||
<el-button type="primary" @click="save" :loading="loading">提交保存</el-button>
|
<el-button type="primary" @click="save" :loading="loading">提交保存</el-button>
|
||||||
<el-button class="ml-3" @click="test">连接测试</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -161,14 +185,16 @@ const save = () => {
|
|||||||
.then(() => ElMessage.success('保存成功'))
|
.then(() => ElMessage.success('保存成功'))
|
||||||
.catch((e) => ElMessage.error(e.message))
|
.catch((e) => ElMessage.error(e.message))
|
||||||
}
|
}
|
||||||
|
|
||||||
const test = () => {
|
|
||||||
ElMessage.info('请在对象存储端验证配置')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.settings {
|
.settings {
|
||||||
|
a {
|
||||||
|
color: #409eff;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
.el-form-item__label {
|
.el-form-item__label {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user