A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 427 lines 9.4 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 api 18 19import ( 20 "net/http" 21 "strings" 22 "time" 23 24 "github.com/88250/gulu" 25 "github.com/gin-gonic/gin" 26 "github.com/siyuan-note/siyuan/kernel/model" 27 "github.com/siyuan-note/siyuan/kernel/treenode" 28 "github.com/siyuan-note/siyuan/kernel/util" 29) 30 31func getNotebookInfo(c *gin.Context) { 32 ret := gulu.Ret.NewResult() 33 defer c.JSON(http.StatusOK, ret) 34 35 arg, ok := util.JsonArg(c, ret) 36 if !ok { 37 return 38 } 39 40 boxID := arg["notebook"].(string) 41 if util.InvalidIDPattern(boxID, ret) { 42 return 43 } 44 45 box := model.Conf.Box(boxID) 46 if nil == box { 47 ret.Code = -1 48 ret.Msg = "notebook [" + boxID + "] not found" 49 return 50 } 51 52 boxInfo := box.GetInfo() 53 ret.Data = map[string]interface{}{ 54 "boxInfo": boxInfo, 55 } 56} 57 58func setNotebookIcon(c *gin.Context) { 59 ret := gulu.Ret.NewResult() 60 defer c.JSON(http.StatusOK, ret) 61 62 arg, ok := util.JsonArg(c, ret) 63 if !ok { 64 return 65 } 66 67 boxID := arg["notebook"].(string) 68 icon := arg["icon"].(string) 69 model.SetBoxIcon(boxID, icon) 70} 71 72func changeSortNotebook(c *gin.Context) { 73 ret := gulu.Ret.NewResult() 74 defer c.JSON(http.StatusOK, ret) 75 76 arg, ok := util.JsonArg(c, ret) 77 if !ok { 78 return 79 } 80 81 idsArg := arg["notebooks"].([]interface{}) 82 var ids []string 83 for _, p := range idsArg { 84 ids = append(ids, p.(string)) 85 } 86 model.ChangeBoxSort(ids) 87} 88 89func renameNotebook(c *gin.Context) { 90 ret := gulu.Ret.NewResult() 91 defer c.JSON(http.StatusOK, ret) 92 93 arg, ok := util.JsonArg(c, ret) 94 if !ok { 95 return 96 } 97 98 notebook := arg["notebook"].(string) 99 if util.InvalidIDPattern(notebook, ret) { 100 return 101 } 102 103 name := arg["name"].(string) 104 err := model.RenameBox(notebook, name) 105 if err != nil { 106 ret.Code = -1 107 ret.Msg = err.Error() 108 ret.Data = map[string]interface{}{"closeTimeout": 5000} 109 return 110 } 111 112 evt := util.NewCmdResult("renamenotebook", 0, util.PushModeBroadcast) 113 evt.Data = map[string]interface{}{ 114 "box": notebook, 115 "name": name, 116 } 117 util.PushEvent(evt) 118} 119 120func removeNotebook(c *gin.Context) { 121 ret := gulu.Ret.NewResult() 122 defer c.JSON(http.StatusOK, ret) 123 124 arg, ok := util.JsonArg(c, ret) 125 if !ok { 126 return 127 } 128 129 notebook := arg["notebook"].(string) 130 if util.InvalidIDPattern(notebook, ret) { 131 return 132 } 133 134 if util.ReadOnly && !model.IsUserGuide(notebook) { 135 ret.Code = -1 136 ret.Msg = model.Conf.Language(34) 137 ret.Data = map[string]interface{}{"closeTimeout": 5000} 138 return 139 } 140 141 err := model.RemoveBox(notebook) 142 if err != nil { 143 ret.Code = -1 144 ret.Msg = err.Error() 145 return 146 } 147 148 evt := util.NewCmdResult("unmount", 0, util.PushModeBroadcast) 149 evt.Data = map[string]interface{}{ 150 "box": notebook, 151 } 152 evt.Callback = arg["callback"] 153 util.PushEvent(evt) 154} 155 156func createNotebook(c *gin.Context) { 157 ret := gulu.Ret.NewResult() 158 defer c.JSON(http.StatusOK, ret) 159 160 arg, ok := util.JsonArg(c, ret) 161 if !ok { 162 return 163 } 164 165 name := arg["name"].(string) 166 id, err := model.CreateBox(name) 167 if err != nil { 168 ret.Code = -1 169 ret.Msg = err.Error() 170 return 171 } 172 173 existed, err := model.Mount(id) 174 if err != nil { 175 ret.Code = -1 176 ret.Msg = err.Error() 177 return 178 } 179 180 box := model.Conf.Box(id) 181 if nil == box { 182 ret.Code = -1 183 ret.Msg = "opened notebook [" + id + "] not found" 184 return 185 } 186 187 ret.Data = map[string]interface{}{ 188 "notebook": box, 189 } 190 191 evt := util.NewCmdResult("createnotebook", 0, util.PushModeBroadcast) 192 evt.Data = map[string]interface{}{ 193 "box": box, 194 "existed": existed, 195 } 196 util.PushEvent(evt) 197} 198 199func openNotebook(c *gin.Context) { 200 ret := gulu.Ret.NewResult() 201 defer c.JSON(http.StatusOK, ret) 202 203 arg, ok := util.JsonArg(c, ret) 204 if !ok { 205 return 206 } 207 208 notebook := arg["notebook"].(string) 209 if util.InvalidIDPattern(notebook, ret) { 210 return 211 } 212 213 isUserGuide := model.IsUserGuide(notebook) 214 if util.ReadOnly && !isUserGuide { 215 ret.Code = -1 216 ret.Msg = model.Conf.Language(34) 217 ret.Data = map[string]interface{}{"closeTimeout": 5000} 218 return 219 } 220 221 if isUserGuide && util.ContainerIOS == util.Container { 222 // iOS 端不再支持打开用户指南,请参考桌面端用户指南 223 // 用户指南中包含了付费相关内容,无法通过商店上架审核 224 // Opening the user guide is no longer supported on iOS https://github.com/siyuan-note/siyuan/issues/11492 225 ret.Code = -1 226 ret.Msg = model.Conf.Language(215) 227 ret.Data = map[string]interface{}{"closeTimeout": 7000} 228 return 229 } 230 231 msgId := util.PushMsg(model.Conf.Language(45), 1000*60*15) 232 defer util.PushClearMsg(msgId) 233 existed, err := model.Mount(notebook) 234 if err != nil { 235 ret.Code = -1 236 ret.Msg = err.Error() 237 return 238 } 239 240 box := model.Conf.Box(notebook) 241 if nil == box { 242 ret.Code = -1 243 ret.Msg = "opened notebook [" + notebook + "] not found" 244 return 245 } 246 247 evt := util.NewCmdResult("mount", 0, util.PushModeBroadcast) 248 evt.Data = map[string]interface{}{ 249 "box": box, 250 "existed": existed, 251 } 252 evt.Callback = arg["callback"] 253 util.PushEvent(evt) 254 255 if isUserGuide { 256 appArg := arg["app"] 257 app := "" 258 if nil != appArg { 259 app = appArg.(string) 260 } 261 262 go func() { 263 var startID string 264 i := 0 265 for ; i < 70; i++ { 266 time.Sleep(100 * time.Millisecond) 267 guideStartID := map[string]string{ 268 "20210808180117-czj9bvb": "20200812220555-lj3enxa", 269 "20211226090932-5lcq56f": "20211226115423-d5z1joq", 270 "20210808180117-6v0mkxr": "20200923234011-ieuun1p", 271 "20240530133126-axarxgx": "20240530101000-4qitucx", 272 } 273 startID = guideStartID[notebook] 274 if treenode.ExistBlockTree(startID) { 275 util.BroadcastByTypeAndApp("main", app, "openFileById", 0, "", map[string]interface{}{ 276 "id": startID, 277 }) 278 break 279 } 280 } 281 }() 282 } 283} 284 285func closeNotebook(c *gin.Context) { 286 ret := gulu.Ret.NewResult() 287 defer c.JSON(http.StatusOK, ret) 288 289 arg, ok := util.JsonArg(c, ret) 290 if !ok { 291 return 292 } 293 294 notebook := arg["notebook"].(string) 295 if util.InvalidIDPattern(notebook, ret) { 296 return 297 } 298 model.Unmount(notebook) 299} 300 301func getNotebookConf(c *gin.Context) { 302 ret := gulu.Ret.NewResult() 303 defer c.JSON(http.StatusOK, ret) 304 305 arg, ok := util.JsonArg(c, ret) 306 if !ok { 307 return 308 } 309 310 notebook := arg["notebook"].(string) 311 if util.InvalidIDPattern(notebook, ret) { 312 return 313 } 314 315 box := model.Conf.GetBox(notebook) 316 if nil == box { 317 ret.Code = -1 318 ret.Msg = "notebook [" + notebook + "] not found" 319 return 320 } 321 322 ret.Data = map[string]interface{}{ 323 "box": box.ID, 324 "name": box.Name, 325 "conf": box.GetConf(), 326 } 327} 328 329func setNotebookConf(c *gin.Context) { 330 ret := gulu.Ret.NewResult() 331 defer c.JSON(http.StatusOK, ret) 332 333 arg, ok := util.JsonArg(c, ret) 334 if !ok { 335 return 336 } 337 338 notebook := arg["notebook"].(string) 339 if util.InvalidIDPattern(notebook, ret) { 340 return 341 } 342 343 box := model.Conf.GetBox(notebook) 344 if nil == box { 345 ret.Code = -1 346 ret.Msg = "notebook [" + notebook + "] not found" 347 return 348 } 349 350 param, err := gulu.JSON.MarshalJSON(arg["conf"]) 351 if err != nil { 352 ret.Code = -1 353 ret.Msg = err.Error() 354 return 355 } 356 357 boxConf := box.GetConf() 358 if err = gulu.JSON.UnmarshalJSON(param, boxConf); err != nil { 359 ret.Code = -1 360 ret.Msg = err.Error() 361 return 362 } 363 364 boxConf.RefCreateSavePath = util.TrimSpaceInPath(boxConf.RefCreateSavePath) 365 if "" != boxConf.RefCreateSavePath { 366 if !strings.HasSuffix(boxConf.RefCreateSavePath, "/") { 367 boxConf.RefCreateSavePath += "/" 368 } 369 } 370 371 boxConf.DailyNoteSavePath = util.TrimSpaceInPath(boxConf.DailyNoteSavePath) 372 if "" != boxConf.DailyNoteSavePath { 373 if !strings.HasPrefix(boxConf.DailyNoteSavePath, "/") { 374 boxConf.DailyNoteSavePath = "/" + boxConf.DailyNoteSavePath 375 } 376 } 377 if "/" == boxConf.DailyNoteSavePath { 378 ret.Code = -1 379 ret.Msg = model.Conf.Language(49) 380 return 381 } 382 383 boxConf.DailyNoteTemplatePath = util.TrimSpaceInPath(boxConf.DailyNoteTemplatePath) 384 if "" != boxConf.DailyNoteTemplatePath { 385 if !strings.HasSuffix(boxConf.DailyNoteTemplatePath, ".md") { 386 boxConf.DailyNoteTemplatePath += ".md" 387 } 388 if !strings.HasPrefix(boxConf.DailyNoteTemplatePath, "/") { 389 boxConf.DailyNoteTemplatePath = "/" + boxConf.DailyNoteTemplatePath 390 } 391 } 392 393 boxConf.DocCreateSavePath = util.TrimSpaceInPath(boxConf.DocCreateSavePath) 394 395 box.SaveConf(boxConf) 396 ret.Data = boxConf 397} 398 399func lsNotebooks(c *gin.Context) { 400 ret := gulu.Ret.NewResult() 401 defer c.JSON(http.StatusOK, ret) 402 403 flashcard := false 404 405 // 兼容旧版接口,不能直接使用 util.JsonArg() 406 arg := map[string]interface{}{} 407 if err := c.ShouldBindJSON(&arg); err == nil { 408 if arg["flashcard"] != nil { 409 flashcard = arg["flashcard"].(bool) 410 } 411 } 412 413 var notebooks []*model.Box 414 if flashcard { 415 notebooks = model.GetFlashcardNotebooks() 416 } else { 417 var err error 418 notebooks, err = model.ListNotebooks() 419 if err != nil { 420 return 421 } 422 } 423 424 ret.Data = map[string]interface{}{ 425 "notebooks": notebooks, 426 } 427}