path.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. package util
  17. import (
  18. "bytes"
  19. "io/fs"
  20. "net"
  21. "os"
  22. "path"
  23. "path/filepath"
  24. "sort"
  25. "strings"
  26. "time"
  27. "github.com/88250/gulu"
  28. "github.com/siyuan-note/filelock"
  29. "github.com/siyuan-note/logging"
  30. )
  31. var (
  32. SSL = false
  33. UserAgent = "SiYuan/" + Ver
  34. )
  35. func GetTreeID(treePath string) string {
  36. return strings.TrimSuffix(path.Base(treePath), ".sy")
  37. }
  38. func ShortPathForBootingDisplay(p string) string {
  39. if 25 > len(p) {
  40. return p
  41. }
  42. p = strings.TrimSuffix(p, ".sy")
  43. p = path.Base(p)
  44. return p
  45. }
  46. var LocalIPs []string
  47. func GetLocalIPs() (ret []string) {
  48. if ContainerAndroid == Container || ContainerHarmony == Container {
  49. // Android 上用不了 net.InterfaceAddrs() https://github.com/golang/go/issues/40569,所以前面使用启动内核传入的参数 localIPs
  50. LocalIPs = append(LocalIPs, LocalHost)
  51. LocalIPs = gulu.Str.RemoveDuplicatedElem(LocalIPs)
  52. return LocalIPs
  53. }
  54. ret = []string{}
  55. addrs, err := net.InterfaceAddrs()
  56. if err != nil {
  57. logging.LogWarnf("get interface addresses failed: %s", err)
  58. return
  59. }
  60. IPv4Nets := []*net.IPNet{}
  61. IPv6Nets := []*net.IPNet{}
  62. for _, addr := range addrs {
  63. if networkIp, ok := addr.(*net.IPNet); ok && networkIp.IP.String() != "<nil>" {
  64. if networkIp.IP.To4() != nil {
  65. IPv4Nets = append(IPv4Nets, networkIp)
  66. } else if networkIp.IP.To16() != nil {
  67. IPv6Nets = append(IPv6Nets, networkIp)
  68. }
  69. }
  70. }
  71. // loopback address
  72. for _, net := range IPv4Nets {
  73. if net.IP.IsLoopback() {
  74. ret = append(ret, net.IP.String())
  75. }
  76. }
  77. // private address
  78. for _, net := range IPv4Nets {
  79. if net.IP.IsPrivate() {
  80. ret = append(ret, net.IP.String())
  81. }
  82. }
  83. // IPv4 private address
  84. for _, net := range IPv4Nets {
  85. if net.IP.IsGlobalUnicast() {
  86. ret = append(ret, net.IP.String())
  87. }
  88. }
  89. // link-local unicast address
  90. for _, net := range IPv4Nets {
  91. if net.IP.IsLinkLocalUnicast() {
  92. ret = append(ret, net.IP.String())
  93. }
  94. }
  95. // loopback address
  96. for _, net := range IPv6Nets {
  97. if net.IP.IsLoopback() {
  98. ret = append(ret, "["+net.IP.String()+"]")
  99. }
  100. }
  101. // private address
  102. for _, net := range IPv6Nets {
  103. if net.IP.IsPrivate() {
  104. ret = append(ret, "["+net.IP.String()+"]")
  105. }
  106. }
  107. // IPv6 private address
  108. for _, net := range IPv6Nets {
  109. if net.IP.IsGlobalUnicast() {
  110. ret = append(ret, "["+net.IP.String()+"]")
  111. }
  112. }
  113. // link-local unicast address
  114. for _, net := range IPv6Nets {
  115. if net.IP.IsLinkLocalUnicast() {
  116. ret = append(ret, "["+net.IP.String()+"]")
  117. }
  118. }
  119. ret = append(ret, LocalHost)
  120. ret = gulu.Str.RemoveDuplicatedElem(ret)
  121. return
  122. }
  123. func isRunningInDockerContainer() bool {
  124. if _, runInContainer := os.LookupEnv("RUN_IN_CONTAINER"); runInContainer {
  125. return true
  126. }
  127. if _, err := os.Stat("/.dockerenv"); err == nil {
  128. return true
  129. }
  130. return false
  131. }
  132. func IsRelativePath(dest string) bool {
  133. if 1 > len(dest) {
  134. return true
  135. }
  136. if '/' == dest[0] {
  137. return false
  138. }
  139. return !strings.Contains(dest, ":/") && !strings.Contains(dest, ":\\")
  140. }
  141. func TimeFromID(id string) (ret string) {
  142. if 14 > len(id) {
  143. logging.LogWarnf("invalid id [%s], stack [\n%s]", id, logging.ShortStack())
  144. return time.Now().Format("20060102150405")
  145. }
  146. ret = id[:14]
  147. return
  148. }
  149. func GetChildDocDepth(treeAbsPath string) (ret int) {
  150. dir := strings.TrimSuffix(treeAbsPath, ".sy")
  151. if !gulu.File.IsDir(dir) {
  152. return
  153. }
  154. baseDepth := strings.Count(filepath.ToSlash(treeAbsPath), "/")
  155. depth := 1
  156. filelock.Walk(dir, func(path string, d fs.DirEntry, err error) error {
  157. p := filepath.ToSlash(path)
  158. currentDepth := strings.Count(p, "/")
  159. if depth < currentDepth {
  160. depth = currentDepth
  161. }
  162. return nil
  163. })
  164. ret = depth - baseDepth
  165. return
  166. }
  167. func NormalizeConcurrentReqs(concurrentReqs int, provider int) int {
  168. if 1 > concurrentReqs {
  169. if 2 == provider { // S3
  170. return 8
  171. } else if 3 == provider { // WebDAV
  172. return 1
  173. }
  174. return 8
  175. }
  176. if 16 < concurrentReqs {
  177. return 16
  178. }
  179. return concurrentReqs
  180. }
  181. func NormalizeTimeout(timeout int) int {
  182. if 7 > timeout {
  183. if 1 > timeout {
  184. return 60
  185. }
  186. return 7
  187. }
  188. if 300 < timeout {
  189. return 300
  190. }
  191. return timeout
  192. }
  193. func NormalizeEndpoint(endpoint string) string {
  194. endpoint = strings.TrimSpace(endpoint)
  195. if "" == endpoint {
  196. return ""
  197. }
  198. if !strings.HasPrefix(endpoint, "http://") && !strings.HasPrefix(endpoint, "https://") {
  199. endpoint = "http://" + endpoint
  200. }
  201. if !strings.HasSuffix(endpoint, "/") {
  202. endpoint = endpoint + "/"
  203. }
  204. return endpoint
  205. }
  206. func FilterMoveDocFromPaths(fromPaths []string, toPath string) (ret []string) {
  207. tmp := FilterSelfChildDocs(fromPaths)
  208. for _, fromPath := range tmp {
  209. fromDir := strings.TrimSuffix(fromPath, ".sy")
  210. if strings.HasPrefix(toPath, fromDir) {
  211. continue
  212. }
  213. ret = append(ret, fromPath)
  214. }
  215. return
  216. }
  217. func FilterSelfChildDocs(paths []string) (ret []string) {
  218. sort.Slice(paths, func(i, j int) bool { return strings.Count(paths[i], "/") < strings.Count(paths[j], "/") })
  219. dirs := map[string]string{}
  220. for _, fromPath := range paths {
  221. dir := strings.TrimSuffix(fromPath, ".sy")
  222. existParent := false
  223. for d, _ := range dirs {
  224. if strings.HasPrefix(fromPath, d) {
  225. existParent = true
  226. break
  227. }
  228. }
  229. if existParent {
  230. continue
  231. }
  232. dirs[dir] = fromPath
  233. ret = append(ret, fromPath)
  234. }
  235. return
  236. }
  237. func IsAssetLinkDest(dest []byte) bool {
  238. return bytes.HasPrefix(dest, []byte("assets/"))
  239. }
  240. var (
  241. SiYuanAssetsImage = []string{".apng", ".ico", ".cur", ".jpg", ".jpe", ".jpeg", ".jfif", ".pjp", ".pjpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".avif"}
  242. SiYuanAssetsAudio = []string{".mp3", ".wav", ".ogg", ".m4a", ".flac"}
  243. SiYuanAssetsVideo = []string{".mov", ".weba", ".mkv", ".mp4", ".webm"}
  244. )
  245. func IsDisplayableAsset(p string) bool {
  246. ext := strings.ToLower(filepath.Ext(p))
  247. if "" == ext {
  248. return false
  249. }
  250. if gulu.Str.Contains(ext, SiYuanAssetsImage) {
  251. return true
  252. }
  253. if gulu.Str.Contains(ext, SiYuanAssetsAudio) {
  254. return true
  255. }
  256. if gulu.Str.Contains(ext, SiYuanAssetsVideo) {
  257. return true
  258. }
  259. return false
  260. }
  261. func GetAbsPathInWorkspace(relPath string) (string, error) {
  262. absPath := filepath.Join(WorkspaceDir, relPath)
  263. absPath = filepath.Clean(absPath)
  264. if WorkspaceDir == absPath {
  265. return absPath, nil
  266. }
  267. if IsSubPath(WorkspaceDir, absPath) {
  268. return absPath, nil
  269. }
  270. return "", os.ErrPermission
  271. }