siyuan/kernel/util/path.go
Yingyi / 颖逸 ba2193403d
Support read-only publish service
* 🎨 kernel supports read-only publishing services

* 🐛 Fix authentication vulnerabilities

* 🎨 Protect secret information

* 🎨 Adjust the permission control

* 🎨 Adjust the permission control

* 🎨 Fixed the vulnerability that `getFile` gets file `conf.json`

* 🎨 Add API `/api/setting/setPublish`

* 🎨 Add API `/api/setting/getPublish`

* 🐛 Fixed the issue that PWA-related files could not pass BasicAuth

* 🎨 Add a settings panel for publishing features

* 📝 Add guide for `Publish Service`

* 📝 Update Japanese user guide

* 🎨 Merge fixed static file services
2024-06-12 21:03:51 +08:00

281 lines
6.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SiYuan - Refactor your thinking
// 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"
"path/filepath"
"sort"
"strings"
"time"
"github.com/88250/gulu"
"github.com/siyuan-note/filelock"
"github.com/siyuan-note/logging"
)
var (
SSL = false
UserAgent = "SiYuan/" + Ver
)
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) {
if ContainerAndroid == Container {
// Android 上用不了 net.InterfaceAddrs() https://github.com/golang/go/issues/40569所以前面使用启动内核传入的参数 localIPs
LocalIPs = append(LocalIPs, LocalHost)
LocalIPs = gulu.Str.RemoveDuplicatedElem(LocalIPs)
return LocalIPs
}
ret = []string{}
addrs, err := net.InterfaceAddrs()
if nil != err {
logging.LogWarnf("get interface addresses failed: %s", err)
return
}
IPv4Nets := []*net.IPNet{}
IPv6Nets := []*net.IPNet{}
for _, addr := range addrs {
if networkIp, ok := addr.(*net.IPNet); ok && networkIp.IP.String() != "<nil>" {
if networkIp.IP.To4() != nil {
IPv4Nets = append(IPv4Nets, networkIp)
} else if networkIp.IP.To16() != nil {
IPv6Nets = append(IPv6Nets, networkIp)
}
}
}
// loopback address
for _, net := range IPv4Nets {
if net.IP.IsLoopback() {
ret = append(ret, net.IP.String())
}
}
// private address
for _, net := range IPv4Nets {
if net.IP.IsPrivate() {
ret = append(ret, net.IP.String())
}
}
// IPv4 private address
for _, net := range IPv4Nets {
if net.IP.IsGlobalUnicast() {
ret = append(ret, net.IP.String())
}
}
// link-local unicast address
for _, net := range IPv4Nets {
if net.IP.IsLinkLocalUnicast() {
ret = append(ret, net.IP.String())
}
}
// loopback address
for _, net := range IPv6Nets {
if net.IP.IsLoopback() {
ret = append(ret, "["+net.IP.String()+"]")
}
}
// private address
for _, net := range IPv6Nets {
if net.IP.IsPrivate() {
ret = append(ret, "["+net.IP.String()+"]")
}
}
// IPv6 private address
for _, net := range IPv6Nets {
if net.IP.IsGlobalUnicast() {
ret = append(ret, "["+net.IP.String()+"]")
}
}
// link-local unicast address
for _, net := range IPv6Nets {
if net.IP.IsLinkLocalUnicast() {
ret = append(ret, "["+net.IP.String()+"]")
}
}
ret = append(ret, LocalHost)
ret = gulu.Str.RemoveDuplicatedElem(ret)
return
}
func isRunningInDockerContainer() bool {
if _, runInContainer := os.LookupEnv("RUN_IN_CONTAINER"); runInContainer {
return true
}
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) {
if 14 > len(id) {
logging.LogWarnf("invalid id [%s], stack [\n%s]", id, logging.ShortStack())
return time.Now().Format("20060102150405")
}
ret = id[:14]
return
}
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
filelock.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
}
func NormalizeTimeout(timeout int) int {
if 7 > timeout {
if 1 > timeout {
return 60
}
return 7
}
if 300 < timeout {
return 300
}
return timeout
}
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
}
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
}
func FilterSelfChildDocs(paths []string) (ret []string) {
sort.Slice(paths, func(i, j int) bool { return strings.Count(paths[i], "/") < strings.Count(paths[j], "/") })
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
}
func IsAssetLinkDest(dest []byte) bool {
return bytes.HasPrefix(dest, []byte("assets/"))
}
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
}
func GetAbsPathInWorkspace(relPath string) (string, error) {
absPath := filepath.Join(WorkspaceDir, relPath)
absPath = filepath.Clean(absPath)
if WorkspaceDir == absPath {
return absPath, nil
}
if IsSubPath(WorkspaceDir, absPath) {
return absPath, nil
}
return "", os.ErrPermission
}