2022-05-26 15:18:53 +08:00
|
|
|
|
// SiYuan - Build Your Eternal Digital Garden
|
|
|
|
|
// Copyright (c) 2020-present, b3log.org
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
package util
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"net"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
2022-10-31 22:42:30 +08:00
|
|
|
|
"path/filepath"
|
2022-11-03 18:48:11 +08:00
|
|
|
|
"sort"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
"strings"
|
2022-09-18 10:56:13 +08:00
|
|
|
|
"time"
|
2022-06-23 19:35:59 +08:00
|
|
|
|
|
|
|
|
|
"github.com/88250/gulu"
|
2022-07-17 12:22:32 +08:00
|
|
|
|
"github.com/siyuan-note/logging"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
SSL = false
|
|
|
|
|
UserAgent = "SiYuan/" + Ver
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2023-06-19 21:27:53 +08:00
|
|
|
|
ChinaServer = "https://siyuan-sync.b3logfile.com" // 中国大陆云端服务地址,阿里云负载均衡,用于接口,数据同步文件上传、下载会走七牛云 OSS ChinaSyncServer
|
|
|
|
|
ChinaWebSocketServer = "wss://siyuan-sync.b3logfile.com" // 中国大陆云端服务地址,阿里云负载均衡,用于接口,数据同步文件上传、下载会走七牛云 OSS ChinaSyncServer
|
|
|
|
|
ChinaSyncServer = "https://siyuan-data.b3logfile.com/" // 中国大陆云端数据同步服务地址,七牛云 OSS,用于数据同步文件上传、下载
|
|
|
|
|
ChinaCloudAssetsServer = "https://assets.b3logfile.com/siyuan/" // 中国大陆云端图床服务地址,用于导出预览模式下订阅会员渲染图床
|
|
|
|
|
ChinaAccountServer = "https://ld246.com" // 中国大陆链滴服务地址,用于账号登录、分享发布帖子
|
|
|
|
|
|
|
|
|
|
NorthAmericaServer = "https://siyuan-cloud.liuyun.io" // 北美云端服务地址,Cloudflare
|
|
|
|
|
NorthAmericaWebSocketServer = "wss://siyuan-cloud.liuyun.io" // 北美云端服务地址,Cloudflare,用于接口,数据同步文件上传、下载会走七牛云 OSS ChinaSyncServer
|
|
|
|
|
NorthAmericaSyncServer = "https://siyuan-data.liuyun.io/" // 北美云端数据同步服务地址,七牛云 OSS,用于数据同步文件上传、下载
|
|
|
|
|
NorthAmericaCloudAssetsServer = "https://assets.liuyun.io/siyuan/" // 北美云端图床服务地址,用于导出预览模式下订阅会员渲染图床
|
|
|
|
|
NorthAmericaAccountServer = "https://liuyun.io" // 流云服务地址,用于账号登录、分享发布帖子
|
|
|
|
|
|
|
|
|
|
BazaarStatServer = "http://bazaar.b3logfile.com" // 集市包统计服务地址,直接对接 Bucket 没有 CDN
|
|
|
|
|
BazaarOSSServer = "https://oss.b3logfile.com" // 云端对象存储地址,七牛云,仅用于读取集市包,全球 CDN
|
2022-05-26 15:18:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func ShortPathForBootingDisplay(p string) string {
|
|
|
|
|
if 25 > len(p) {
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
p = strings.TrimSuffix(p, ".sy")
|
|
|
|
|
p = path.Base(p)
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var LocalIPs []string
|
|
|
|
|
|
|
|
|
|
func GetLocalIPs() (ret []string) {
|
2022-08-31 12:25:55 +08:00
|
|
|
|
if ContainerAndroid == Container {
|
2022-06-13 20:57:18 +08:00
|
|
|
|
// Android 上用不了 net.InterfaceAddrs() https://github.com/golang/go/issues/40569,所以前面使用启动内核传入的参数 localIPs
|
2022-10-27 09:47:03 +08:00
|
|
|
|
LocalIPs = append(LocalIPs, LocalHost)
|
2022-06-23 19:35:59 +08:00
|
|
|
|
LocalIPs = gulu.Str.RemoveDuplicatedElem(LocalIPs)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return LocalIPs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = []string{}
|
2022-06-13 20:57:18 +08:00
|
|
|
|
addrs, err := net.InterfaceAddrs()
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if nil != err {
|
2022-07-17 12:22:32 +08:00
|
|
|
|
logging.LogWarnf("get interface addresses failed: %s", err)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
|
if networkIp, ok := addr.(*net.IPNet); ok && !networkIp.IP.IsLoopback() && networkIp.IP.To4() != nil &&
|
|
|
|
|
bytes.Equal([]byte{255, 255, 255, 0}, networkIp.Mask) {
|
|
|
|
|
ret = append(ret, networkIp.IP.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-27 09:47:03 +08:00
|
|
|
|
ret = append(ret, LocalHost)
|
2022-06-23 19:35:59 +08:00
|
|
|
|
ret = gulu.Str.RemoveDuplicatedElem(ret)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isRunningInDockerContainer() bool {
|
2022-08-29 00:07:42 +08:00
|
|
|
|
if _, runInContainer := os.LookupEnv("RUN_IN_CONTAINER"); runInContainer {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if _, err := os.Stat("/.dockerenv"); err == nil {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func IsRelativePath(dest string) bool {
|
|
|
|
|
if 1 > len(dest) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if '/' == dest[0] {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return !strings.Contains(dest, ":/") && !strings.Contains(dest, ":\\")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TimeFromID(id string) (ret string) {
|
2022-09-18 10:56:13 +08:00
|
|
|
|
if 14 > len(id) {
|
|
|
|
|
logging.LogWarnf("invalid id [%s], stack [\n%s]", id, logging.ShortStack())
|
|
|
|
|
return time.Now().Format("20060102150405")
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
ret = id[:14]
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-10-31 22:42:30 +08:00
|
|
|
|
|
|
|
|
|
func GetChildDocDepth(treeAbsPath string) (ret int) {
|
|
|
|
|
dir := strings.TrimSuffix(treeAbsPath, ".sy")
|
|
|
|
|
if !gulu.File.IsDir(dir) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
baseDepth := strings.Count(filepath.ToSlash(treeAbsPath), "/")
|
|
|
|
|
depth := 1
|
|
|
|
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
|
|
|
p := filepath.ToSlash(path)
|
|
|
|
|
currentDepth := strings.Count(p, "/")
|
|
|
|
|
if depth < currentDepth {
|
|
|
|
|
depth = currentDepth
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
ret = depth - baseDepth
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-11-02 23:33:05 +08:00
|
|
|
|
|
2022-12-04 20:42:35 +08:00
|
|
|
|
func NormalizeTimeout(timeout int) int {
|
|
|
|
|
if 7 > timeout {
|
|
|
|
|
if 1 > timeout {
|
|
|
|
|
return 30
|
|
|
|
|
}
|
|
|
|
|
return 7
|
|
|
|
|
}
|
|
|
|
|
if 300 < timeout {
|
|
|
|
|
return 300
|
|
|
|
|
}
|
|
|
|
|
return timeout
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 23:33:05 +08:00
|
|
|
|
func NormalizeEndpoint(endpoint string) string {
|
|
|
|
|
endpoint = strings.TrimSpace(endpoint)
|
|
|
|
|
if "" == endpoint {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasPrefix(endpoint, "http://") && !strings.HasPrefix(endpoint, "https://") {
|
|
|
|
|
endpoint = "http://" + endpoint
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasSuffix(endpoint, "/") {
|
|
|
|
|
endpoint = endpoint + "/"
|
|
|
|
|
}
|
|
|
|
|
return endpoint
|
|
|
|
|
}
|
2022-11-03 18:48:11 +08:00
|
|
|
|
|
2022-11-05 22:59:24 +08:00
|
|
|
|
func FilterMoveDocFromPaths(fromPaths []string, toPath string) (ret []string) {
|
|
|
|
|
tmp := FilterSelfChildDocs(fromPaths)
|
|
|
|
|
for _, fromPath := range tmp {
|
|
|
|
|
fromDir := strings.TrimSuffix(fromPath, ".sy")
|
|
|
|
|
if strings.HasPrefix(toPath, fromDir) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ret = append(ret, fromPath)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 18:48:11 +08:00
|
|
|
|
func FilterSelfChildDocs(paths []string) (ret []string) {
|
2022-11-04 15:35:22 +08:00
|
|
|
|
sort.Slice(paths, func(i, j int) bool { return strings.Count(paths[i], "/") < strings.Count(paths[j], "/") })
|
2022-11-03 18:48:11 +08:00
|
|
|
|
|
|
|
|
|
dirs := map[string]string{}
|
|
|
|
|
for _, fromPath := range paths {
|
|
|
|
|
dir := strings.TrimSuffix(fromPath, ".sy")
|
|
|
|
|
existParent := false
|
|
|
|
|
for d, _ := range dirs {
|
|
|
|
|
if strings.HasPrefix(fromPath, d) {
|
|
|
|
|
existParent = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if existParent {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
dirs[dir] = fromPath
|
|
|
|
|
ret = append(ret, fromPath)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-11-11 11:51:14 +08:00
|
|
|
|
|
|
|
|
|
func IsAssetLinkDest(dest []byte) bool {
|
|
|
|
|
return bytes.HasPrefix(dest, []byte("assets/"))
|
|
|
|
|
}
|
2023-06-03 17:42:01 +08:00
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
SiYuanAssetsImage = []string{".apng", ".ico", ".cur", ".jpg", ".jpe", ".jpeg", ".jfif", ".pjp", ".pjpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".avif"}
|
|
|
|
|
SiYuanAssetsAudio = []string{".mp3", ".wav", ".ogg", ".m4a"}
|
|
|
|
|
SiYuanAssetsVideo = []string{".mov", ".weba", ".mkv", ".mp4", ".webm"}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func IsDisplayableAsset(p string) bool {
|
|
|
|
|
ext := strings.ToLower(filepath.Ext(p))
|
|
|
|
|
if "" == ext {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if gulu.Str.Contains(ext, SiYuanAssetsImage) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if gulu.Str.Contains(ext, SiYuanAssetsAudio) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if gulu.Str.Contains(ext, SiYuanAssetsVideo) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|