diff --git a/frontend/src/components/FileSystem/components/FileEditor/BgmBar.vue b/frontend/src/components/FileSystem/components/FileEditor/BgmBar.vue index 5650aab..5f9c456 100644 --- a/frontend/src/components/FileSystem/components/FileEditor/BgmBar.vue +++ b/frontend/src/components/FileSystem/components/FileEditor/BgmBar.vue @@ -155,7 +155,6 @@ const _restoreLastPlayed = () => { if (!track) return lastPlayedPath.value = saved playingPath.value = saved - emit('switch', track) } restorePlaylist().then(_restoreLastPlayed) diff --git a/internal/filesystem/asset_handler.go b/internal/filesystem/asset_handler.go index 31d5c2c..d8a2bd5 100644 --- a/internal/filesystem/asset_handler.go +++ b/internal/filesystem/asset_handler.go @@ -898,7 +898,10 @@ var sa=Element.prototype.setAttribute; Element.prototype.setAttribute=function(n,v){if((n==='src'||n==='href'||n==='data'||n==='poster')&&typeof v==='string')v=rw(v);return sa.call(this,n,v);}; try{var d=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,'src');Object.defineProperty(HTMLScriptElement.prototype,'src',{set:function(v){d.set.call(this,rw(v))},get:d.get,configurable:true});}catch(e){} try{var d2=Object.getOwnPropertyDescriptor(HTMLLinkElement.prototype,'href');Object.defineProperty(HTMLLinkElement.prototype,'href',{set:function(v){d2.set.call(this,rw(v))},get:d2.get,configurable:true});}catch(e){} -})(); + try{var d3=Object.getOwnPropertyDescriptor(HTMLImageElement.prototype,'src');Object.defineProperty(HTMLImageElement.prototype,'src',{set:function(v){d3.set.call(this,rw(v))},get:d3.get,configurable:true});}catch(e){} + try{var d4=Object.getOwnPropertyDescriptor(HTMLSourceElement.prototype,'src');Object.defineProperty(HTMLSourceElement.prototype,'src',{set:function(v){d4.set.call(this,rw(v))},get:d4.get,configurable:true});}catch(e){} + try{var d5=Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype,'src');Object.defineProperty(HTMLIFrameElement.prototype,'src',{set:function(v){d5.set.call(this,rw(v))},get:d5.get,configurable:true});}catch(e){} + })(); ` // 在 后立即插入(确保在任何其他脚本之前执行) diff --git a/internal/filesystem/site_resource.go b/internal/filesystem/site_resource.go new file mode 100644 index 0000000..efaddc8 --- /dev/null +++ b/internal/filesystem/site_resource.go @@ -0,0 +1,47 @@ +package filesystem + +import ( + "regexp" + "strings" +) + +var ( + htmlResRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`) + htmlCssUrlRe = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`) +) + +// ExtractHtmlResources 从 HTML 内容提取资源路径 +func ExtractHtmlResources(html string) []string { + seen := make(map[string]bool) + var resources []string + add := func(v string) { + v = strings.TrimSpace(v) + if v != "" && !seen[v] { + seen[v] = true + resources = append(resources, v) + } + } + for _, m := range htmlResRegex.FindAllStringSubmatch(html, -1) { + if len(m) > 1 { + add(m[1]) + } + } + for _, m := range htmlCssUrlRe.FindAllStringSubmatch(html, -1) { + if len(m) > 1 { + add(m[1]) + } + } + return resources +} + +// ShouldSkipResource 判断资源路径是否应跳过 +func ShouldSkipResource(p string) bool { + return strings.HasPrefix(p, "data:") || + strings.HasPrefix(p, "http://") || + strings.HasPrefix(p, "https://") || + strings.HasPrefix(p, "//") || + strings.HasPrefix(p, "#") || + strings.HasPrefix(p, "javascript:") || + strings.HasPrefix(p, "mailto:") || + strings.HasPrefix(p, "blob:") +} diff --git a/internal/oss/qiniu/client.go b/internal/oss/qiniu/client.go index 0b22778..3dd0e20 100644 --- a/internal/oss/qiniu/client.go +++ b/internal/oss/qiniu/client.go @@ -480,6 +480,9 @@ func (c *Client) ListFiles(ctx context.Context, options *oss.ListOptions) (*oss. if options.Marker != "" { path += "&marker=" + options.Marker } + if options.Delimiter != "" { + path += "&delimiter=" + options.Delimiter + } // 使用 GET 方法和 RSF API resp, err := c.doRSFRequest("GET", path) diff --git a/internal/ossdrv/service.go b/internal/ossdrv/service.go index 2233cdd..2d55db1 100644 --- a/internal/ossdrv/service.go +++ b/internal/ossdrv/service.go @@ -8,7 +8,6 @@ import ( "os" "path" "path/filepath" - "regexp" "strings" "sync" "time" @@ -640,9 +639,7 @@ func (s *Service) RenamePath(connID string, oldPath string, newPath string) (*fi return result, nil } -// htmlResourceRegex 提取 HTML 资源引用的正则 -var htmlResourceRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`) -var htmlCssUrlRegex = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`) + // DownloadSiteForPreview 下载 HTML 及其引用的资源到临时目录 // 对绝对路径(/开头)从 HTML 目录逐级向上嗅探网站根目录 @@ -695,7 +692,7 @@ func (s *Service) DownloadSiteForPreview(connID string, rawPath string) (string, if err != nil { return htmlLocalPath, nil // HTML 已下载,资源解析失败不影响 } - resources := extractHtmlResources(string(htmlContent)) + resources := filesystem.ExtractHtmlResources(string(htmlContent)) // 4. 下载资源 htmlOssDir := keyDir @@ -716,7 +713,7 @@ func (s *Service) DownloadSiteForPreview(connID string, rawPath string) (string, } for _, resPath := range resources { - if shouldSkipResource(resPath) { + if filesystem.ShouldSkipResource(resPath) { continue } @@ -817,57 +814,19 @@ func supplementDir(c oss.OSSProvider, ctx context.Context, remoteDir string, tmp prefix := remoteDir + "/" result, err := c.ListFiles(ctx, &oss.ListOptions{Prefix: prefix, MaxKeys: 200}) if err != nil { - return + return } for _, f := range result.Files { if strings.HasSuffix(f.Key, "/") || f.Size == 0 { continue } - relPath := strings.TrimPrefix(f.Key, siteRoot) - localPath := filepath.Join(tmpDir, filepath.FromSlash(relPath)) + localPath := filepath.Join(tmpDir, filepath.FromSlash(f.Key)) if _, err := os.Stat(localPath); err == nil { continue } downloadResource(c, ctx, f.Key, localPath) } } -func extractHtmlResources(html string) []string { - seen := make(map[string]bool) - var resources []string - - add := func(v string) { - v = strings.TrimSpace(v) - if v != "" && !seen[v] { - seen[v] = true - resources = append(resources, v) - } - } - - for _, m := range htmlResourceRegex.FindAllStringSubmatch(html, -1) { - if len(m) > 1 { - add(m[1]) - } - } - for _, m := range htmlCssUrlRegex.FindAllStringSubmatch(html, -1) { - if len(m) > 1 { - add(m[1]) - } - } - - return resources -} - -// shouldSkipResource 判断资源路径是否应跳过 -func shouldSkipResource(p string) bool { - return strings.HasPrefix(p, "data:") || - strings.HasPrefix(p, "http://") || - strings.HasPrefix(p, "https://") || - strings.HasPrefix(p, "//") || - strings.HasPrefix(p, "#") || - strings.HasPrefix(p, "javascript:") || - strings.HasPrefix(p, "mailto:") || - strings.HasPrefix(p, "blob:") -} // DownloadToTemp 下载文件到本地临时目录(带 SQLite 缓存) func (s *Service) DownloadToTemp(connID string, rawPath string) (string, error) { diff --git a/internal/sftp/service.go b/internal/sftp/service.go index a63253a..62ddd1b 100644 --- a/internal/sftp/service.go +++ b/internal/sftp/service.go @@ -8,7 +8,6 @@ import ( "os" "path" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -19,10 +18,7 @@ import ( sftpclient "github.com/pkg/sftp" ) -var ( - sftpResRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`) - sftpCssUrlRe = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`) -) + // Service SFTP 文件操作服务 type Service struct { @@ -383,7 +379,7 @@ func (s *Service) DownloadSiteForPreview(connID string, remotePath string) (stri if err != nil { return htmlLocalPath, nil } - resources := sftpExtractResources(string(htmlContent)) + resources := filesystem.ExtractHtmlResources(string(htmlContent)) // 5. 下载静态引用资源(嗅探网站根) htmlRemoteDir := keyDir @@ -404,7 +400,7 @@ func (s *Service) DownloadSiteForPreview(connID string, remotePath string) (stri } for _, resPath := range resources { - if sftpShouldSkip(resPath) { + if filesystem.ShouldSkipResource(resPath) { continue } isAbsolute := strings.HasPrefix(resPath, "/") @@ -513,9 +509,7 @@ func sftpSupplementDir(s *Service, c *Client, remoteDir string, tmpDir string, s continue } fullPath := path.Join(remoteDir, entry.Name()) - relPath := strings.TrimPrefix(fullPath, siteRoot) - relPath = strings.TrimPrefix(relPath, "/") - localPath := filepath.Join(tmpDir, filepath.FromSlash(relPath)) + localPath := filepath.Join(tmpDir, filepath.FromSlash(fullPath)) if _, err := os.Stat(localPath); err == nil { continue } @@ -707,38 +701,3 @@ func toFileOperationResult(m map[string]interface{}, isDir bool) *filesystem.Fil } } -// sftpExtractResources 从 HTML 内容提取资源路径 -func sftpExtractResources(html string) []string { - seen := make(map[string]bool) - var resources []string - add := func(v string) { - v = strings.TrimSpace(v) - if v != "" && !seen[v] { - seen[v] = true - resources = append(resources, v) - } - } - for _, m := range sftpResRegex.FindAllStringSubmatch(html, -1) { - if len(m) > 1 { - add(m[1]) - } - } - for _, m := range sftpCssUrlRe.FindAllStringSubmatch(html, -1) { - if len(m) > 1 { - add(m[1]) - } - } - return resources -} - -// sftpShouldSkip 判断资源路径是否应跳过 -func sftpShouldSkip(p string) bool { - return strings.HasPrefix(p, "data:") || - strings.HasPrefix(p, "http://") || - strings.HasPrefix(p, "https://") || - strings.HasPrefix(p, "//") || - strings.HasPrefix(p, "#") || - strings.HasPrefix(p, "javascript:") || - strings.HasPrefix(p, "mailto:") || - strings.HasPrefix(p, "blob:") -}