diff --git a/.gitignore b/.gitignore index 0581e88..b347139 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ service/conf service/lang service/database service/web -service/uploads \ No newline at end of file +service/uploads +service/plugins \ No newline at end of file diff --git a/add-frontend-version.js b/add-frontend-version.js index d55b9a5..46b5d97 100644 --- a/add-frontend-version.js +++ b/add-frontend-version.js @@ -4,7 +4,9 @@ const moment = require('moment') // git 最新标签 // const latestTag = execSync('git describe --tags --abbrev=0').toString().trim() -const packDate = moment().format('YYYYMMDD-HH') + +// 设置默认时区为 'Asia/Shanghai' +const packDate = moment().utc().format('YYYYMMDD') // 要追加的内容 const contentToAppend = `\nVITE_APP_VERSION=${packDate}` diff --git a/service/api/api_v1/panel/itemIcon.go b/service/api/api_v1/panel/itemIcon.go index 4b7bf07..10795f1 100644 --- a/service/api/api_v1/panel/itemIcon.go +++ b/service/api/api_v1/panel/itemIcon.go @@ -2,13 +2,20 @@ package panel import ( "encoding/json" + "fmt" + "net/url" + "os" + "path" + "strings" "sun-panel/api/api_v1/common/apiData/commonApiStructs" "sun-panel/api/api_v1/common/apiData/panelApiStructs" "sun-panel/api/api_v1/common/apiReturn" "sun-panel/api/api_v1/common/base" "sun-panel/global" + "sun-panel/lib/cmn" "sun-panel/lib/siteFavicon" "sun-panel/models" + "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" @@ -188,7 +195,9 @@ func (a *ItemIcon) SaveSort(c *gin.Context) { apiReturn.Success(c) } +// 支持获取并直接下载对方网站图标到服务器 func (a *ItemIcon) GetSiteFavicon(c *gin.Context) { + userInfo, _ := base.GetCurrentUserInfo(c) req := panelApiStructs.ItemIconGetSiteFaviconReq{} if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { @@ -196,10 +205,68 @@ func (a *ItemIcon) GetSiteFavicon(c *gin.Context) { return } resp := panelApiStructs.ItemIconGetSiteFaviconResp{} - if iconUrl, ok := siteFavicon.GetOneFaviconURL(req.Url); ok { - resp.IconUrl = iconUrl - apiReturn.SuccessData(c, resp) + fullUrl := "" + if iconUrl, err := siteFavicon.GetOneFaviconURL(req.Url); err != nil { + apiReturn.Error(c, "acquisition failed: get ico error:"+err.Error()) + return + } else { + fullUrl = iconUrl + } + + parsedURL, err := url.Parse(req.Url) + if err != nil { + apiReturn.Error(c, "acquisition failed:"+err.Error()) + return + } + + protocol := parsedURL.Scheme + global.Logger.Debug("protocol:", protocol) + global.Logger.Debug("fullUrl:", fullUrl) + + // 如果URL以双斜杠(//)开头,则使用当前页面协议 + if strings.HasPrefix(fullUrl, "//") { + fullUrl = protocol + "://" + fullUrl[2:] + } else if !strings.HasPrefix(fullUrl, "http://") && !strings.HasPrefix(fullUrl, "https://") { + // 如果URL既不以http://开头也不以https://开头,则默认为http协议 + fullUrl = "http://" + fullUrl + } + global.Logger.Debug("fullUrl:", fullUrl) + // 去除图标的get参数 + { + parsedIcoURL, err := url.Parse(fullUrl) + if err != nil { + apiReturn.Error(c, "acquisition failed: parsed ico URL :"+err.Error()) + return + } + fullUrl = parsedIcoURL.Scheme + "://" + parsedIcoURL.Host + parsedIcoURL.Path + } + global.Logger.Debug("fullUrl:", fullUrl) + + // 生成保存目录 + configUpload := global.Config.GetValueString("base", "source_path") + savePath := fmt.Sprintf("%s/%d/%d/%d/", configUpload, time.Now().Year(), time.Now().Month(), time.Now().Day()) + isExist, _ := cmn.PathExists(savePath) + if !isExist { + os.MkdirAll(savePath, os.ModePerm) + } + + // 下载 + var imgInfo *os.File + { + var err error + if imgInfo, err = siteFavicon.DownloadImage(fullUrl, savePath, 1024*1024); err != nil { + apiReturn.Error(c, "acquisition failed: download"+err.Error()) + return + } + } + + // 保存到数据库 + ext := path.Ext(fullUrl) + mFile := models.File{} + if _, err := mFile.AddFile(userInfo.ID, parsedURL.Host, ext, imgInfo.Name()); err != nil { + apiReturn.ErrorDatabase(c, err.Error()) return } - apiReturn.Error(c, "acquisition failed") + resp.IconUrl = imgInfo.Name()[1:] + apiReturn.SuccessData(c, resp) } diff --git a/service/assets/version b/service/assets/version index aff4283..7a8e32d 100644 --- a/service/assets/version +++ b/service/assets/version @@ -1 +1 @@ -9|1.3.0-beta24-01-25 \ No newline at end of file +10|1.3.0 \ No newline at end of file diff --git a/service/lib/siteFavicon/favico.go b/service/lib/siteFavicon/favico.go index bc69f4a..38e1af7 100644 --- a/service/lib/siteFavicon/favico.go +++ b/service/lib/siteFavicon/favico.go @@ -2,11 +2,17 @@ package siteFavicon import ( "errors" + "fmt" + "io" "net/http" "net/url" + "os" + "path" "regexp" "strconv" "strings" + "sun-panel/lib/cmn" + "time" "github.com/PuerkitoBio/goquery" ) @@ -20,7 +26,91 @@ func IsHTTPURL(url string) bool { return match } -func GetOneFaviconURL(urlStr string) (string, bool) { +func GetOneFaviconURL(urlStr string) (string, error) { + iconURLs, err := getFaviconURL(urlStr) + if err != nil { + return "", err + } + + for _, v := range iconURLs { + // 标准的路径地址 + if IsHTTPURL(v) { + return v, nil + } else { + urlInfo, _ := url.Parse(urlStr) + fullUrl := urlInfo.Scheme + "://" + urlInfo.Host + "/" + strings.TrimPrefix(v, "/") + return fullUrl, nil + } + } + return "", fmt.Errorf("not found ico") +} + +// 获取远程文件的大小 +func GetRemoteFileSize(url string) (int64, error) { + resp, err := http.Head(url) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + // 检查HTTP响应状态 + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("HTTP request failed, status code: %d", resp.StatusCode) + } + + // 获取Content-Length字段,即文件大小 + size := resp.ContentLength + return size, nil +} + +// 下载图片 +func DownloadImage(url, savePath string, maxSize int64) (*os.File, error) { + // 获取远程文件大小 + fileSize, err := GetRemoteFileSize(url) + if err != nil { + return nil, err + } + + // 判断文件大小是否在阈值内 + if fileSize > maxSize { + return nil, fmt.Errorf("文件太大,不下载。大小:%d字节", fileSize) + } + + // 发送HTTP GET请求获取图片数据 + response, err := http.Get(url) + if err != nil { + return nil, err + } + defer response.Body.Close() + + // 检查HTTP响应状态 + if response.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP request failed, status code: %d", response.StatusCode) + } + + urlFileName := path.Base(url) + fileExt := path.Ext(url) + fileName := cmn.Md5(fmt.Sprintf("%s%s", urlFileName, time.Now().String())) + fileExt + + destination := savePath + "/" + fileName + + // 创建本地文件用于保存图片 + file, err := os.Create(destination) + if err != nil { + return nil, err + } + defer file.Close() + + // 将图片数据写入本地文件 + _, err = io.Copy(file, response.Body) + if err != nil { + return nil, err + } + return file, nil +} + +func GetOneFaviconURLAndUpload(urlStr string) (string, bool) { + //www.iqiyipic.com/pcwimg/128-128-logo.png iconURLs, err := getFaviconURL(urlStr) if err != nil { return "", false diff --git a/src/views/home/index.vue b/src/views/home/index.vue index d5e5765..c09fc76 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -574,7 +574,11 @@ function handleAddItem(itemIconGroupId?: number) {
- +
+ + + +