mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-16 14:40:12 +01:00
🧑💻 Kernel serve WebDAV service on path /webdav/ (#12412)
* 🎨 Add a WebDAV service to the kernel * 🎨 Add more writable WebDAV methods
This commit is contained in:
parent
f88296c4d2
commit
9cff5cc235
6 changed files with 65 additions and 5 deletions
|
|
@ -71,6 +71,7 @@ require (
|
||||||
golang.org/x/image v0.19.0
|
golang.org/x/image v0.19.0
|
||||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
|
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
|
||||||
golang.org/x/mod v0.20.0
|
golang.org/x/mod v0.20.0
|
||||||
|
golang.org/x/net v0.28.0
|
||||||
golang.org/x/text v0.17.0
|
golang.org/x/text v0.17.0
|
||||||
golang.org/x/time v0.6.0
|
golang.org/x/time v0.6.0
|
||||||
)
|
)
|
||||||
|
|
@ -164,7 +165,6 @@ require (
|
||||||
golang.org/x/arch v0.9.0 // indirect
|
golang.org/x/arch v0.9.0 // indirect
|
||||||
golang.org/x/crypto v0.26.0 // indirect
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
|
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
|
||||||
golang.org/x/net v0.28.0 // indirect
|
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.24.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
|
|
|
||||||
|
|
@ -441,7 +441,7 @@ func ExportData() (zipPath string, err error) {
|
||||||
util.PushEndlessProgress(Conf.Language(65))
|
util.PushEndlessProgress(Conf.Language(65))
|
||||||
defer util.ClearPushProgress(100)
|
defer util.ClearPushProgress(100)
|
||||||
|
|
||||||
name := util.FilterFileName(filepath.Base(util.WorkspaceDir)) + "-" + util.CurrentTimeSecondsStr()
|
name := util.FilterFileName(util.WorkspaceName) + "-" + util.CurrentTimeSecondsStr()
|
||||||
exportFolder := filepath.Join(util.TempDir, "export", name)
|
exportFolder := filepath.Join(util.TempDir, "export", name)
|
||||||
zipPath, err = exportData(exportFolder)
|
zipPath, err = exportData(exportFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,16 @@ func CheckAuth(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过 BasicAuth (header: Authorization)
|
||||||
|
if username, password, ok := c.Request.BasicAuth(); ok {
|
||||||
|
// 使用访问授权码作为密码
|
||||||
|
if util.WorkspaceName == username && Conf.AccessAuthCode == password {
|
||||||
|
c.Set(RoleContextKey, RoleAdministrator)
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 通过 API token (header: Authorization)
|
// 通过 API token (header: Authorization)
|
||||||
if authHeader := c.GetHeader("Authorization"); "" != authHeader {
|
if authHeader := c.GetHeader("Authorization"); "" != authHeader {
|
||||||
var token string
|
var token string
|
||||||
|
|
@ -289,7 +299,15 @@ func CheckAuth(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if "/check-auth" == c.Request.URL.Path { // 跳过访问授权页
|
// WebDAV BasicAuth Authenticate
|
||||||
|
if strings.HasPrefix(c.Request.RequestURI, "/webdav") {
|
||||||
|
c.Header("WWW-Authenticate", "Basic realm=Authorization Required")
|
||||||
|
c.AbortWithStatus(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过访问授权页
|
||||||
|
if "/check-auth" == c.Request.URL.Path {
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,21 @@ import (
|
||||||
"github.com/siyuan-note/siyuan/kernel/model"
|
"github.com/siyuan-note/siyuan/kernel/model"
|
||||||
"github.com/siyuan-note/siyuan/kernel/server/proxy"
|
"github.com/siyuan-note/siyuan/kernel/server/proxy"
|
||||||
"github.com/siyuan-note/siyuan/kernel/util"
|
"github.com/siyuan-note/siyuan/kernel/util"
|
||||||
|
"golang.org/x/net/webdav"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cookieStore = cookie.NewStore([]byte("ATN51UlxVq1Gcvdf"))
|
cookieStore = cookie.NewStore([]byte("ATN51UlxVq1Gcvdf"))
|
||||||
|
WebDavMethod = []string{
|
||||||
|
"OPTIONS",
|
||||||
|
"GET", "HEAD",
|
||||||
|
"POST", "PUT",
|
||||||
|
"DELETE",
|
||||||
|
"MKCOL",
|
||||||
|
"COPY", "MOVE",
|
||||||
|
"LOCK", "UNLOCK",
|
||||||
|
"PROPFIND", "PROPPATCH",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Serve(fastMode bool) {
|
func Serve(fastMode bool) {
|
||||||
|
|
@ -76,6 +87,7 @@ func Serve(fastMode bool) {
|
||||||
serveAssets(ginServer)
|
serveAssets(ginServer)
|
||||||
serveAppearance(ginServer)
|
serveAppearance(ginServer)
|
||||||
serveWebSocket(ginServer)
|
serveWebSocket(ginServer)
|
||||||
|
serveWebDAV(ginServer)
|
||||||
serveExport(ginServer)
|
serveExport(ginServer)
|
||||||
serveWidgets(ginServer)
|
serveWidgets(ginServer)
|
||||||
servePlugins(ginServer)
|
servePlugins(ginServer)
|
||||||
|
|
@ -371,7 +383,7 @@ func serveAuthPage(c *gin.Context) {
|
||||||
"l8": model.Conf.Language(95),
|
"l8": model.Conf.Language(95),
|
||||||
"appearanceMode": model.Conf.Appearance.Mode,
|
"appearanceMode": model.Conf.Appearance.Mode,
|
||||||
"appearanceModeOS": model.Conf.Appearance.ModeOS,
|
"appearanceModeOS": model.Conf.Appearance.ModeOS,
|
||||||
"workspace": filepath.Base(util.WorkspaceDir),
|
"workspace": util.WorkspaceName,
|
||||||
"workspacePath": util.WorkspaceDir,
|
"workspacePath": util.WorkspaceDir,
|
||||||
"keymapGeneralToggleWin": keymapHideWindow,
|
"keymapGeneralToggleWin": keymapHideWindow,
|
||||||
"trayMenuLangs": util.TrayMenuLangs[util.Lang],
|
"trayMenuLangs": util.TrayMenuLangs[util.Lang],
|
||||||
|
|
@ -589,6 +601,33 @@ func serveWebSocket(ginServer *gin.Engine) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serveWebDAV(ginServer *gin.Engine) {
|
||||||
|
// REF: https://github.com/fungaren/gin-webdav
|
||||||
|
handler := webdav.Handler{
|
||||||
|
Prefix: "/webdav/",
|
||||||
|
FileSystem: webdav.Dir(util.WorkspaceDir),
|
||||||
|
LockSystem: webdav.NewMemLS(),
|
||||||
|
Logger: func(r *http.Request, err error) {
|
||||||
|
if nil != err {
|
||||||
|
logging.LogErrorf("WebDAV [%s %s]: %s", r.Method, r.URL.String(), err.Error())
|
||||||
|
}
|
||||||
|
// logging.LogDebugf("WebDAV [%s %s]", r.Method, r.URL.String())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ginGroup := ginServer.Group("/webdav", model.CheckAuth, model.CheckAdminRole)
|
||||||
|
ginGroup.Match(WebDavMethod, "/*path", func(c *gin.Context) {
|
||||||
|
if util.ReadOnly {
|
||||||
|
switch c.Request.Method {
|
||||||
|
case "POST", "PUT", "DELETE", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", "PROPPATCH":
|
||||||
|
c.AbortWithError(http.StatusForbidden, fmt.Errorf(model.Conf.Language(34)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.ServeHTTP(c.Writer, c.Request)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func shortReqMsg(msg []byte) []byte {
|
func shortReqMsg(msg []byte) []byte {
|
||||||
s := gulu.Str.FromBytes(msg)
|
s := gulu.Str.FromBytes(msg)
|
||||||
max := 128
|
max := 128
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ var (
|
||||||
WorkingDir, _ = os.Getwd()
|
WorkingDir, _ = os.Getwd()
|
||||||
|
|
||||||
WorkspaceDir string // 工作空间目录路径
|
WorkspaceDir string // 工作空间目录路径
|
||||||
|
WorkspaceName string // 工作空间名称
|
||||||
WorkspaceLock *flock.Flock // 工作空间锁
|
WorkspaceLock *flock.Flock // 工作空间锁
|
||||||
ConfDir string // 配置目录路径
|
ConfDir string // 配置目录路径
|
||||||
DataDir string // 数据目录路径
|
DataDir string // 数据目录路径
|
||||||
|
|
@ -269,6 +270,7 @@ func initWorkspaceDir(workspaceArg string) {
|
||||||
os.Exit(logging.ExitCodeInitWorkspaceErr)
|
os.Exit(logging.ExitCodeInitWorkspaceErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorkspaceName = filepath.Base(WorkspaceDir)
|
||||||
ConfDir = filepath.Join(WorkspaceDir, "conf")
|
ConfDir = filepath.Join(WorkspaceDir, "conf")
|
||||||
DataDir = filepath.Join(WorkspaceDir, "data")
|
DataDir = filepath.Join(WorkspaceDir, "data")
|
||||||
RepoDir = filepath.Join(WorkspaceDir, "repo")
|
RepoDir = filepath.Join(WorkspaceDir, "repo")
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ func initWorkspaceDirMobile(workspaceBaseDir string) {
|
||||||
os.Exit(logging.ExitCodeInitWorkspaceErr)
|
os.Exit(logging.ExitCodeInitWorkspaceErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorkspaceName = filepath.Base(WorkspaceDir)
|
||||||
ConfDir = filepath.Join(WorkspaceDir, "conf")
|
ConfDir = filepath.Join(WorkspaceDir, "conf")
|
||||||
DataDir = filepath.Join(WorkspaceDir, "data")
|
DataDir = filepath.Join(WorkspaceDir, "data")
|
||||||
RepoDir = filepath.Join(WorkspaceDir, "repo")
|
RepoDir = filepath.Join(WorkspaceDir, "repo")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue