diff --git a/app/electron/main.js b/app/electron/main.js
index 30f58a4eb..6090d5dbb 100644
--- a/app/electron/main.js
+++ b/app/electron/main.js
@@ -67,6 +67,36 @@ if (process.platform === "linux") {
app.commandLine.appendSwitch("wayland-text-input-version", "3");
}
+app.setAsDefaultProtocolClient("siyuan");
+
+app.commandLine.appendSwitch("disable-web-security");
+app.commandLine.appendSwitch("auto-detect", "false");
+app.commandLine.appendSwitch("no-proxy-server");
+app.commandLine.appendSwitch("enable-features", "PlatformHEVCDecoderSupport");
+app.commandLine.appendSwitch("xdg-portal-required-version", "4");
+
+// Support set Chromium command line arguments on the desktop https://github.com/siyuan-note/siyuan/issues/9696
+writeLog("app is packaged [" + app.isPackaged + "], command line args [" + process.argv.join(", ") + "]");
+let argStart = 1;
+if (!app.isPackaged) {
+ argStart = 2;
+}
+
+for (let i = argStart; i < process.argv.length; i++) {
+ let arg = process.argv[i];
+ if (arg.startsWith("--workspace=") || arg.startsWith("--openAsHidden") || arg.startsWith("--port=") || arg.startsWith("siyuan://")) {
+ // 跳过内置参数
+ if (arg.startsWith("--openAsHidden")) {
+ openAsHidden = true;
+ writeLog("open as hidden");
+ }
+ continue;
+ }
+
+ app.commandLine.appendSwitch(arg);
+ writeLog("command line switch [" + arg + "]");
+}
+
try {
firstOpen = !fs.existsSync(path.join(confDir, "workspace.json"));
if (!fs.existsSync(confDir)) {
@@ -297,28 +327,6 @@ const showErrorWindow = (titleZh, titleEn, content, emoji = "⚠️") => {
return errWindow.id;
};
-const writeLog = (out) => {
- console.log(out);
- const logFile = path.join(confDir, "app.log");
- let log = "";
- const maxLogLines = 1024;
- try {
- if (fs.existsSync(logFile)) {
- log = fs.readFileSync(logFile).toString();
- let lines = log.split("\n");
- if (maxLogLines < lines.length) {
- log = lines.slice(maxLogLines / 2, maxLogLines).join("\n") + "\n";
- }
- }
- out = out.toString();
- out = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "") + " " + out;
- log += out + "\n";
- fs.writeFileSync(logFile, log);
- } catch (e) {
- console.error(e);
- }
-};
-
let openAsHidden = false;
const isOpenAsHidden = function () {
return 1 === workspaces.length && openAsHidden;
@@ -730,36 +738,6 @@ const initKernel = (workspace, port, lang) => {
});
};
-app.setAsDefaultProtocolClient("siyuan");
-
-app.commandLine.appendSwitch("disable-web-security");
-app.commandLine.appendSwitch("auto-detect", "false");
-app.commandLine.appendSwitch("no-proxy-server");
-app.commandLine.appendSwitch("enable-features", "PlatformHEVCDecoderSupport");
-app.commandLine.appendSwitch("xdg-portal-required-version", "4");
-
-// Support set Chromium command line arguments on the desktop https://github.com/siyuan-note/siyuan/issues/9696
-writeLog("app is packaged [" + app.isPackaged + "], command line args [" + process.argv.join(", ") + "]");
-let argStart = 1;
-if (!app.isPackaged) {
- argStart = 2;
-}
-
-for (let i = argStart; i < process.argv.length; i++) {
- let arg = process.argv[i];
- if (arg.startsWith("--workspace=") || arg.startsWith("--openAsHidden") || arg.startsWith("--port=") || arg.startsWith("siyuan://")) {
- // 跳过内置参数
- if (arg.startsWith("--openAsHidden")) {
- openAsHidden = true;
- writeLog("open as hidden");
- }
- continue;
- }
-
- app.commandLine.appendSwitch(arg);
- writeLog("command line switch [" + arg + "]");
-}
-
app.whenReady().then(() => {
const resetTrayMenu = (tray, lang, mainWindow) => {
if (!mainWindow || mainWindow.isDestroyed()) {
@@ -1542,3 +1520,26 @@ app.on("before-quit", (event) => {
}
});
});
+
+
+function writeLog(out) {
+ console.log(out);
+ const logFile = path.join(confDir, "app.log");
+ let log = "";
+ const maxLogLines = 1024;
+ try {
+ if (fs.existsSync(logFile)) {
+ log = fs.readFileSync(logFile).toString();
+ let lines = log.split("\n");
+ if (maxLogLines < lines.length) {
+ log = lines.slice(maxLogLines / 2, maxLogLines).join("\n") + "\n";
+ }
+ }
+ out = out.toString();
+ out = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "") + " " + out;
+ log += out + "\n";
+ fs.writeFileSync(logFile, log);
+ } catch (e) {
+ console.error(e);
+ }
+}
\ No newline at end of file
diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml
index 3cb32bd11..e507a629d 100644
--- a/app/pnpm-lock.yaml
+++ b/app/pnpm-lock.yaml
@@ -1415,6 +1415,10 @@ packages:
resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==}
engines: {node: '>=14.14'}
+ fs-extra@11.3.4:
+ resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==}
+ engines: {node: '>=14.14'}
+
fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'}
@@ -1465,16 +1469,17 @@ packages:
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
@@ -2491,6 +2496,7 @@ packages:
tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
temp-file@3.4.0:
resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==}
@@ -2849,7 +2855,7 @@ snapshots:
dependencies:
cross-dirname: 0.1.0
debug: 4.4.3
- fs-extra: 11.3.3
+ fs-extra: 11.3.4
minimist: 1.2.8
postject: 1.0.0-alpha.6
transitivePeerDependencies:
@@ -4221,6 +4227,13 @@ snapshots:
jsonfile: 6.2.0
universalify: 2.0.1
+ fs-extra@11.3.4:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+ optional: true
+
fs-extra@7.0.1:
dependencies:
graceful-fs: 4.2.11
diff --git a/app/src/index.ts b/app/src/index.ts
index c28901f47..1091a0af2 100644
--- a/app/src/index.ts
+++ b/app/src/index.ts
@@ -192,7 +192,7 @@ export class App {
openFileById({app: this, id: data.data.id, action: [Constants.CB_GET_FOCUS]});
break;
case "exit":
- if (isBrowser()) {
+ if (isBrowser() && !isInMobileApp()) {
window.location.href = "about:blank";
}
}
diff --git a/kernel/api/bazaar.go b/kernel/api/bazaar.go
index 26fbc9872..df965b415 100644
--- a/kernel/api/bazaar.go
+++ b/kernel/api/bazaar.go
@@ -95,7 +95,7 @@ func getBazaarPackageREADME(c *gin.Context) {
return
}
ret.Data = map[string]interface{}{
- "html": model.GetPackageREADME(repoURL, repoHash, pkgType),
+ "html": model.GetBazaarPackageREADME(c.Request.Context(), repoURL, repoHash, pkgType),
}
}
diff --git a/kernel/bazaar/icon.go b/kernel/bazaar/icon.go
index cc5191e5c..a6d7549d3 100644
--- a/kernel/bazaar/icon.go
+++ b/kernel/bazaar/icon.go
@@ -153,7 +153,7 @@ func InstalledIcons() (ret []*Icon) {
packageInstallSizeCache.SetDefault(icon.RepoURL, is)
}
icon.HInstallSize = humanize.BytesCustomCeil(uint64(icon.InstallSize), 2)
- icon.PreferredReadme = loadInstalledReadme(installPath, "/appearance/icons/"+dirName+"/", icon.Readme)
+ icon.PreferredReadme = getInstalledPackageREADME(installPath, "/appearance/icons/"+dirName+"/", icon.Readme)
icon.Outdated = isOutdatedIcon(icon, bazaarIcons)
ret = append(ret, icon)
}
diff --git a/kernel/bazaar/plugin.go b/kernel/bazaar/plugin.go
index 8d159cb0a..190444f69 100644
--- a/kernel/bazaar/plugin.go
+++ b/kernel/bazaar/plugin.go
@@ -189,7 +189,7 @@ func InstalledPlugins(frontend string) (ret []*Plugin) {
packageInstallSizeCache.SetDefault(plugin.RepoURL, is)
}
plugin.HInstallSize = humanize.BytesCustomCeil(uint64(plugin.InstallSize), 2)
- plugin.PreferredReadme = loadInstalledReadme(installPath, "/plugins/"+dirName+"/", plugin.Readme)
+ plugin.PreferredReadme = getInstalledPackageREADME(installPath, "/plugins/"+dirName+"/", plugin.Readme)
plugin.Outdated = isOutdatedPlugin(plugin, bazaarPlugins)
plugin.Incompatible = isIncompatiblePlugin(plugin, frontend)
ret = append(ret, plugin)
diff --git a/kernel/bazaar/readme.go b/kernel/bazaar/readme.go
index ff3a350cb..c68c8f8d4 100644
--- a/kernel/bazaar/readme.go
+++ b/kernel/bazaar/readme.go
@@ -18,26 +18,45 @@ package bazaar
import (
"bytes"
+ "context"
"fmt"
"os"
"path/filepath"
"strings"
+ "github.com/88250/gulu"
"github.com/88250/lute"
+ "github.com/88250/lute/ast"
+ "github.com/88250/lute/parse"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/util"
textUnicode "golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
-func GetPackageREADME(repoURL, repoHash, packageType string) (ret string) {
+// getReadmeFileCandidates 根据包的 README 配置返回去重的按优先级排序的 README 候选文件名列表:当前语言首选、default、README.md。
+func getReadmeFileCandidates(readme LocaleStrings) []string {
+ preferred := getPreferredReadme(readme)
+ defaultName := "README.md"
+ if v := strings.TrimSpace(readme["default"]); v != "" {
+ defaultName = v
+ }
+ return gulu.Str.RemoveDuplicatedElem([]string{preferred, defaultName, "README.md"})
+}
+
+// GetBazaarPackageREADME 获取集市包的在线 README。
+func GetBazaarPackageREADME(ctx context.Context, repoURL, repoHash, packageType string) (ret string) {
repoURLHash := repoURL + "@" + repoHash
stageIndexLock.RLock()
stageIndex := cachedStageIndex[packageType]
stageIndexLock.RUnlock()
- if nil == stageIndex {
- return
+ if stageIndex == nil {
+ var err error
+ stageIndex, err = getStageIndex(ctx, packageType)
+ if err != nil {
+ return
+ }
}
url := strings.TrimPrefix(repoURLHash, "https://github.com/")
@@ -48,113 +67,106 @@ func GetPackageREADME(repoURL, repoHash, packageType string) (ret string) {
break
}
}
- if nil == repo || nil == repo.Package {
+ if repo == nil || repo.Package == nil {
return
}
- readme := getPreferredReadme(repo.Package.Readme)
-
- data, err := downloadPackage(repoURLHash+"/"+readme, false, "")
- if err != nil {
- ret = fmt.Sprintf("Load bazaar package's preferred README(%s) failed: %s", readme, err.Error())
- // 回退到 Default README
- var defaultReadme string
- if len(repo.Package.Readme) > 0 {
- defaultReadme = repo.Package.Readme["default"]
- }
- if "" == strings.TrimSpace(defaultReadme) {
- defaultReadme = "README.md"
- }
- if readme != defaultReadme {
- data, err = downloadPackage(repoURLHash+"/"+defaultReadme, false, "")
- if err != nil {
- ret += fmt.Sprintf("
Load bazaar package's default README(%s) failed: %s", defaultReadme, err.Error())
- }
- }
- // 回退到 README.md
- if err != nil && readme != "README.md" && defaultReadme != "README.md" {
- data, err = downloadPackage(repoURLHash+"/README.md", false, "")
- if err != nil {
- ret += fmt.Sprintf("
Load bazaar package's README.md failed: %s", err.Error())
- return
- }
- } else if err != nil {
- return
+ candidates := getReadmeFileCandidates(repo.Package.Readme)
+ var data []byte
+ var loadErr error
+ var errMsgs []string
+ for _, name := range candidates {
+ data, loadErr = downloadPackage(repoURLHash+"/"+name, false, "")
+ if loadErr == nil {
+ break
}
+ errMsgs = append(errMsgs, fmt.Sprintf("Load bazaar package's README(%s) failed: %s", name, loadErr.Error()))
}
-
- if 2 < len(data) {
- if 255 == data[0] && 254 == data[1] {
- data, _, err = transform.Bytes(textUnicode.UTF16(textUnicode.LittleEndian, textUnicode.ExpectBOM).NewDecoder(), data)
- } else if 254 == data[0] && 255 == data[1] {
- data, _, err = transform.Bytes(textUnicode.UTF16(textUnicode.BigEndian, textUnicode.ExpectBOM).NewDecoder(), data)
- }
- }
-
- ret, err = renderREADME(repoURL, data)
- return
-}
-
-func loadInstalledReadme(installPath, basePath string, readme LocaleStrings) (ret string) {
- readmeFilename := getPreferredReadme(readme)
- readmeData, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
- if nil == readErr {
- ret, _ = renderLocalREADME(basePath, readmeData)
+ if loadErr != nil {
+ ret = strings.Join(errMsgs, "
")
return
}
- logging.LogWarnf("read installed %s failed: %s", readmeFilename, readErr)
- ret = fmt.Sprintf("File %s not found", readmeFilename)
- // 回退到 Default README
- var defaultReadme string
- if len(readme) > 0 {
- defaultReadme = strings.TrimSpace(readme["default"])
- }
- if "" == defaultReadme {
- defaultReadme = "README.md"
- }
- if readmeFilename != defaultReadme {
- readmeData, readErr = os.ReadFile(filepath.Join(installPath, defaultReadme))
- if nil == readErr {
- ret, _ = renderLocalREADME(basePath, readmeData)
- return
+ if len(data) > 2 {
+ var decoded []byte
+ var err error
+ if data[0] == 0xFF && data[1] == 0xFE {
+ decoded, _, err = transform.Bytes(textUnicode.UTF16(textUnicode.LittleEndian, textUnicode.ExpectBOM).NewDecoder(), data)
+ } else if data[0] == 0xFE && data[1] == 0xFF {
+ decoded, _, err = transform.Bytes(textUnicode.UTF16(textUnicode.BigEndian, textUnicode.ExpectBOM).NewDecoder(), data)
}
- logging.LogWarnf("read installed %s failed: %s", defaultReadme, readErr)
- ret += fmt.Sprintf("
File %s not found", defaultReadme)
- }
- // 回退到 README.md
- if nil != readErr && readmeFilename != "README.md" && defaultReadme != "README.md" {
- readmeData, readErr = os.ReadFile(filepath.Join(installPath, "README.md"))
- if nil == readErr {
- ret, _ = renderLocalREADME(basePath, readmeData)
- return
+ if decoded != nil && err == nil {
+ data = decoded
}
- logging.LogWarnf("read installed README.md failed: %s", readErr)
- ret += "
File README.md not found"
}
- return
-}
-func renderREADME(repoURL string, mdData []byte) (ret string, err error) {
- mdData = bytes.TrimPrefix(mdData, []byte("\xef\xbb\xbf"))
- luteEngine := lute.New()
- luteEngine.SetSoftBreak2HardBreak(false)
- luteEngine.SetCodeSyntaxHighlight(false)
linkBase := "https://cdn.jsdelivr.net/gh/" + strings.TrimPrefix(repoURL, "https://github.com/")
+ ret = renderPackageREADME(linkBase, data)
+ return
+}
+
+// getInstalledPackageREADME 获取集市包的本地 README。
+func getInstalledPackageREADME(installPath, linkBase string, readme LocaleStrings) (ret string) {
+ candidates := getReadmeFileCandidates(readme)
+ var errMsgs []string
+ for _, name := range candidates {
+ readmeData, readErr := os.ReadFile(filepath.Join(installPath, name))
+ if readErr == nil {
+ ret = renderPackageREADME(linkBase, readmeData)
+ return
+ }
+ logging.LogWarnf("read installed %s failed: %s", name, readErr)
+ errMsgs = append(errMsgs, fmt.Sprintf("File [%s] not found", name))
+ }
+ ret = strings.Join(errMsgs, "
")
+ return
+}
+
+// renderPackageREADME 渲染 README Markdown 为 HTML。
+func renderPackageREADME(linkBase string, mdData []byte) (ret string) {
+ mdData = bytes.TrimPrefix(mdData, []byte("\xef\xbb\xbf")) // 移除文件开头的 BOM
+ luteEngine := lute.New()
+ luteEngine.SetSoftBreak2HardBreak(false)
+ luteEngine.SetCodeSyntaxHighlight(false)
luteEngine.SetLinkBase(linkBase)
- ret = luteEngine.Md2HTML(string(mdData))
+
+ tree := parse.Parse("", mdData, luteEngine.ParseOptions)
+ normalizeNodesIAL(tree)
+ ret = luteEngine.Tree2HTML(tree, luteEngine.RenderOptions, luteEngine.ParseOptions)
ret = util.LinkTarget(ret, linkBase)
return
}
-func renderLocalREADME(basePath string, mdData []byte) (ret string, err error) {
- mdData = bytes.TrimPrefix(mdData, []byte("\xef\xbb\xbf"))
- luteEngine := lute.New()
- luteEngine.SetSoftBreak2HardBreak(false)
- luteEngine.SetCodeSyntaxHighlight(false)
- linkBase := basePath
- luteEngine.SetLinkBase(linkBase)
- ret = luteEngine.Md2HTML(string(mdData))
- ret = util.LinkTarget(ret, linkBase)
- return
+func normalizeNodesIAL(tree *parse.Tree) {
+ if tree == nil || tree.Root == nil {
+ return
+ }
+
+ ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
+ if !entering {
+ return ast.WalkContinue
+ }
+ if n.Type == ast.NodeCodeBlock {
+ // 代码块添加 code-block 类名以修正样式。
+ n.KramdownIAL = addClassToKramdownIAL(n.KramdownIAL, "code-block")
+ }
+ return ast.WalkContinue
+ })
+}
+
+func addClassToKramdownIAL(ial [][]string, class string) [][]string {
+ for i, attr := range ial {
+ if len(attr) < 2 || attr[0] != "class" {
+ continue
+ }
+ for _, item := range strings.Fields(attr[1]) {
+ if item == class {
+ return ial
+ }
+ }
+ attr[1] = strings.TrimSpace(attr[1] + " " + class)
+ ial[i] = attr
+ return ial
+ }
+ return append(ial, []string{"class", class})
}
diff --git a/kernel/bazaar/template.go b/kernel/bazaar/template.go
index 07cfd3155..dfab139a8 100644
--- a/kernel/bazaar/template.go
+++ b/kernel/bazaar/template.go
@@ -154,7 +154,7 @@ func InstalledTemplates() (ret []*Template) {
packageInstallSizeCache.SetDefault(template.RepoURL, is)
}
template.HInstallSize = humanize.BytesCustomCeil(uint64(template.InstallSize), 2)
- template.PreferredReadme = loadInstalledReadme(installPath, "/templates/"+dirName+"/", template.Readme)
+ template.PreferredReadme = getInstalledPackageREADME(installPath, "/templates/"+dirName+"/", template.Readme)
template.Outdated = isOutdatedTemplate(template, bazaarTemplates)
ret = append(ret, template)
}
diff --git a/kernel/bazaar/theme.go b/kernel/bazaar/theme.go
index efcc38bd8..a2e2f76fd 100644
--- a/kernel/bazaar/theme.go
+++ b/kernel/bazaar/theme.go
@@ -156,7 +156,7 @@ func InstalledThemes() (ret []*Theme) {
packageInstallSizeCache.SetDefault(theme.RepoURL, is)
}
theme.HInstallSize = humanize.BytesCustomCeil(uint64(theme.InstallSize), 2)
- theme.PreferredReadme = loadInstalledReadme(installPath, "/appearance/themes/"+dirName+"/", theme.Readme)
+ theme.PreferredReadme = getInstalledPackageREADME(installPath, "/appearance/themes/"+dirName+"/", theme.Readme)
theme.Outdated = isOutdatedTheme(theme, bazaarThemes)
ret = append(ret, theme)
}
diff --git a/kernel/bazaar/widget.go b/kernel/bazaar/widget.go
index 405f5c1f5..8009a14d8 100644
--- a/kernel/bazaar/widget.go
+++ b/kernel/bazaar/widget.go
@@ -151,7 +151,7 @@ func InstalledWidgets() (ret []*Widget) {
packageInstallSizeCache.SetDefault(widget.RepoURL, is)
}
widget.HInstallSize = humanize.BytesCustomCeil(uint64(widget.InstallSize), 2)
- widget.PreferredReadme = loadInstalledReadme(installPath, "/widgets/"+dirName+"/", widget.Readme)
+ widget.PreferredReadme = getInstalledPackageREADME(installPath, "/widgets/"+dirName+"/", widget.Readme)
widget.Outdated = isOutdatedWidget(widget, bazaarWidgets)
ret = append(ret, widget)
}
diff --git a/kernel/model/bazaar.go b/kernel/model/bazaar.go
index 7eaf4e3ae..a9f3bf488 100644
--- a/kernel/model/bazaar.go
+++ b/kernel/model/bazaar.go
@@ -17,6 +17,7 @@
package model
import (
+ "context"
"errors"
"fmt"
"path"
@@ -192,8 +193,9 @@ func UpdatedPackages(frontend string) (plugins []*bazaar.Plugin, widgets []*baza
return
}
-func GetPackageREADME(repoURL, repoHash, packageType string) (ret string) {
- ret = bazaar.GetPackageREADME(repoURL, repoHash, packageType)
+// GetBazaarPackageREADME 获取集市包的在线 README。
+func GetBazaarPackageREADME(ctx context.Context, repoURL, repoHash, packageType string) (ret string) {
+ ret = bazaar.GetBazaarPackageREADME(ctx, repoURL, repoHash, packageType)
return
}
diff --git a/kernel/model/conf.go b/kernel/model/conf.go
index 16e015fa3..c82de59a6 100644
--- a/kernel/model/conf.go
+++ b/kernel/model/conf.go
@@ -807,7 +807,9 @@ func Close(force, setCurrentWorkspace bool, execInstallPkg int) (exitCode int) {
time.Sleep(30 * time.Second)
}
}
+
closeSyncWebSocket()
+
go func() {
time.Sleep(500 * time.Millisecond)
logging.LogInfof("exited kernel")
diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go
index a94c37be4..eb16f7ab2 100644
--- a/kernel/model/transaction.go
+++ b/kernel/model/transaction.go
@@ -1237,6 +1237,9 @@ func (tx *Transaction) doInsert0(operation *Operation, tree *parse.Tree) (ret *T
insertedNode = insertedNode.FirstChild
}
node.InsertBefore(insertedNode)
+ for _, remain := range remains {
+ node.InsertBefore(remain)
+ }
} else if "" != previousID {
node = treenode.GetNodeInTree(tree, previousID)
if nil == node {
diff --git a/kernel/server/serve.go b/kernel/server/serve.go
index 94897546f..81a217e23 100644
--- a/kernel/server/serve.go
+++ b/kernel/server/serve.go
@@ -318,6 +318,11 @@ func serveExport(ginServer *gin.Engine) {
}
fullPath := filepath.Join(exportBaseDir, decodedPath)
+ if util.IsSensitivePath(fullPath) {
+ logging.LogErrorf("refuse to export sensitive file [%s]", c.Request.URL.Path)
+ c.Status(http.StatusForbidden)
+ return
+ }
fileInfo, err := os.Stat(fullPath)
if os.IsNotExist(err) {
diff --git a/kernel/util/path.go b/kernel/util/path.go
index c60d1eec1..6e45255bf 100644
--- a/kernel/util/path.go
+++ b/kernel/util/path.go
@@ -391,6 +391,12 @@ func IsSensitivePath(p string) bool {
}
}
+ // 工作空间/conf 目录(小写比较)
+ workspaceConfPrefix := strings.ToLower(filepath.Join(WorkspaceDir, "conf"))
+ if strings.HasPrefix(pp, workspaceConfPrefix) {
+ return true
+ }
+
homePrefixes := []string{
strings.ToLower(filepath.Join(HomeDir, ".ssh")),
strings.ToLower(filepath.Join(HomeDir, ".config")),