A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 190 lines 5.9 kB view raw
1// SiYuan - Refactor your thinking 2// Copyright (c) 2020-present, b3log.org 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU Affero General Public License as published by 6// the Free Software Foundation, either version 3 of the License, or 7// (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU Affero General Public License for more details. 13// 14// You should have received a copy of the GNU Affero General Public License 15// along with this program. If not, see <https://www.gnu.org/licenses/>. 16 17package bazaar 18 19import ( 20 "os" 21 "path/filepath" 22 "sort" 23 "strings" 24 25 "github.com/88250/go-humanize" 26 "github.com/siyuan-note/logging" 27 "github.com/siyuan-note/siyuan/kernel/util" 28) 29 30type Theme struct { 31 *Package 32 33 Modes []string `json:"modes"` 34} 35 36// Themes 返回集市主题列表 37func Themes() (ret []*Theme) { 38 ret = []*Theme{} 39 result := getStageAndBazaar("themes") 40 41 if !result.Online { 42 return 43 } 44 if result.StageErr != nil { 45 return 46 } 47 if 1 > len(result.BazaarIndex) { 48 return 49 } 50 51 for _, repo := range result.StageIndex.Repos { 52 if nil == repo.Package { 53 continue 54 } 55 theme := buildThemeFromStageRepo(repo, result.BazaarIndex) 56 if nil != theme { 57 ret = append(ret, theme) 58 } 59 } 60 61 sort.Slice(ret, func(i, j int) bool { return ret[i].Updated > ret[j].Updated }) 62 return 63} 64 65// buildThemeFromStageRepo 使用 stage 内嵌的 package 构建 *Theme,不发起 HTTP 请求。 66func buildThemeFromStageRepo(repo *StageRepo, bazaarIndex map[string]*bazaarPackage) *Theme { 67 pkg := *repo.Package 68 pkg.URL = strings.TrimSuffix(pkg.URL, "/") 69 repoURLHash := strings.Split(repo.URL, "@") 70 if 2 != len(repoURLHash) { 71 return nil 72 } 73 pkg.RepoURL = "https://github.com/" + repoURLHash[0] 74 pkg.RepoHash = repoURLHash[1] 75 pkg.PreviewURL = util.BazaarOSSServer + "/package/" + repo.URL + "/preview.png?imageslim" 76 pkg.PreviewURLThumb = util.BazaarOSSServer + "/package/" + repo.URL + "/preview.png?imageView2/2/w/436/h/232" 77 pkg.IconURL = util.BazaarOSSServer + "/package/" + repo.URL + "/icon.png" 78 pkg.Updated = repo.Updated 79 pkg.Stars = repo.Stars 80 pkg.OpenIssues = repo.OpenIssues 81 pkg.Size = repo.Size 82 pkg.HSize = humanize.BytesCustomCeil(uint64(pkg.Size), 2) 83 pkg.InstallSize = repo.InstallSize 84 pkg.HInstallSize = humanize.BytesCustomCeil(uint64(pkg.InstallSize), 2) 85 pkg.HUpdated = formatUpdated(pkg.Updated) 86 pkg.PreferredFunding = getPreferredFunding(pkg.Funding) 87 pkg.PreferredName = GetPreferredName(&pkg) 88 pkg.PreferredDesc = getPreferredDesc(pkg.Description) 89 pkg.DisallowInstall = disallowInstallBazaarPackage(&pkg) 90 pkg.DisallowUpdate = disallowInstallBazaarPackage(&pkg) 91 pkg.UpdateRequiredMinAppVer = pkg.MinAppVersion 92 if bp := bazaarIndex[repoURLHash[0]]; nil != bp { 93 pkg.Downloads = bp.Downloads 94 } 95 packageInstallSizeCache.SetDefault(pkg.RepoURL, pkg.InstallSize) 96 theme := &Theme{Package: &pkg, Modes: []string{}} 97 return theme 98} 99 100func InstalledThemes() (ret []*Theme) { 101 ret = []*Theme{} 102 103 if !util.IsPathRegularDirOrSymlinkDir(util.ThemesPath) { 104 return 105 } 106 107 themeDirs, err := os.ReadDir(util.ThemesPath) 108 if err != nil { 109 logging.LogWarnf("read appearance themes folder failed: %s", err) 110 return 111 } 112 113 bazaarThemes := Themes() 114 115 for _, themeDir := range themeDirs { 116 if !util.IsDirRegularOrSymlink(themeDir) { 117 continue 118 } 119 dirName := themeDir.Name() 120 if isBuiltInTheme(dirName) { 121 continue 122 } 123 124 theme, parseErr := ThemeJSON(dirName) 125 if nil != parseErr || nil == theme { 126 continue 127 } 128 129 theme.RepoURL = theme.URL 130 theme.DisallowInstall = disallowInstallBazaarPackage(theme.Package) 131 if bazaarPkg := getBazaarTheme(theme.Name, bazaarThemes); nil != bazaarPkg { 132 theme.DisallowUpdate = disallowInstallBazaarPackage(bazaarPkg.Package) 133 theme.UpdateRequiredMinAppVer = bazaarPkg.MinAppVersion 134 theme.RepoURL = bazaarPkg.RepoURL 135 } 136 137 installPath := filepath.Join(util.ThemesPath, dirName) 138 theme.Installed = true 139 theme.PreviewURL = "/appearance/themes/" + dirName + "/preview.png" 140 theme.PreviewURLThumb = "/appearance/themes/" + dirName + "/preview.png" 141 theme.IconURL = "/appearance/themes/" + dirName + "/icon.png" 142 theme.PreferredFunding = getPreferredFunding(theme.Funding) 143 theme.PreferredName = GetPreferredName(theme.Package) 144 theme.PreferredDesc = getPreferredDesc(theme.Description) 145 info, statErr := os.Stat(filepath.Join(installPath, "theme.json")) 146 if nil != statErr { 147 logging.LogWarnf("stat install theme.json failed: %s", statErr) 148 continue 149 } 150 theme.HInstallDate = info.ModTime().Format("2006-01-02") 151 if installSize, ok := packageInstallSizeCache.Get(theme.RepoURL); ok { 152 theme.InstallSize = installSize.(int64) 153 } else { 154 is, _ := util.SizeOfDirectory(installPath) 155 theme.InstallSize = is 156 packageInstallSizeCache.SetDefault(theme.RepoURL, is) 157 } 158 theme.HInstallSize = humanize.BytesCustomCeil(uint64(theme.InstallSize), 2) 159 theme.PreferredReadme = loadInstalledReadme(installPath, "/appearance/themes/"+dirName+"/", theme.Readme) 160 theme.Outdated = isOutdatedTheme(theme, bazaarThemes) 161 ret = append(ret, theme) 162 } 163 return 164} 165 166func isBuiltInTheme(dirName string) bool { 167 return "daylight" == dirName || "midnight" == dirName 168} 169 170func getBazaarTheme(name string, themes []*Theme) *Theme { 171 for _, p := range themes { 172 if p.Name == name { 173 return p 174 } 175 } 176 return nil 177} 178 179func InstallTheme(repoURL, repoHash, installPath string, systemID string) error { 180 repoURLHash := repoURL + "@" + repoHash 181 data, err := downloadPackage(repoURLHash, true, systemID) 182 if err != nil { 183 return err 184 } 185 return installPackage(data, installPath, repoURLHash) 186} 187 188func UninstallTheme(installPath string) error { 189 return uninstallPackage(installPath) 190}