github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/files/notsynchronized.go (about) 1 package files 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 8 "github.com/cozy/cozy-stack/model/permission" 9 "github.com/cozy/cozy-stack/pkg/consts" 10 "github.com/cozy/cozy-stack/pkg/couchdb" 11 "github.com/cozy/cozy-stack/pkg/jsonapi" 12 "github.com/cozy/cozy-stack/web/middlewares" 13 "github.com/labstack/echo/v4" 14 ) 15 16 // AddNotSynchronizedOn is the echo.handler for adding not_synchronized_on to 17 // a directory 18 // POST /files/:file-id/relationships/not_synchronized_on 19 func AddNotSynchronizedOn(c echo.Context) error { 20 instance := middlewares.GetInstance(c) 21 22 fileID := c.Param("file-id") 23 dir, err := instance.VFS().DirByID(fileID) 24 if err != nil { 25 return WrapVfsError(err) 26 } 27 28 if err = middlewares.AllowVFS(c, permission.PATCH, dir); err != nil { 29 return err 30 } 31 32 references, err := jsonapi.BindRelations(c.Request()) 33 if err != nil { 34 return WrapVfsError(err) 35 } 36 37 dir.AddNotSynchronizedOn(references...) 38 updateDirCozyMetadata(c, dir) 39 if err = couchdb.UpdateDoc(instance, dir); err != nil { 40 return WrapVfsError(err) 41 } 42 43 refs := dir.NotSynchronizedOn 44 count := len(refs) 45 meta := jsonapi.Meta{Rev: dir.Rev(), Count: &count} 46 return jsonapi.DataRelations(c, http.StatusOK, refs, &meta, nil, nil) 47 } 48 49 // RemoveNotSynchronizedOn is the echo.handler for removing not_synchronized_on to 50 // a directory 51 // DELETE /files/:file-id/relationships/not_synchronized_on 52 func RemoveNotSynchronizedOn(c echo.Context) error { 53 instance := middlewares.GetInstance(c) 54 55 fileID := c.Param("file-id") 56 dir, err := instance.VFS().DirByID(fileID) 57 if err != nil { 58 return WrapVfsError(err) 59 } 60 61 if err = middlewares.AllowVFS(c, permission.PATCH, dir); err != nil { 62 return err 63 } 64 65 references, err := jsonapi.BindRelations(c.Request()) 66 if err != nil { 67 return WrapVfsError(err) 68 } 69 70 dir.RemoveNotSynchronizedOn(references...) 71 updateDirCozyMetadata(c, dir) 72 if err = couchdb.UpdateDoc(instance, dir); err != nil { 73 return WrapVfsError(err) 74 } 75 76 refs := dir.NotSynchronizedOn 77 count := len(refs) 78 meta := jsonapi.Meta{Rev: dir.Rev(), Count: &count} 79 return jsonapi.DataRelations(c, http.StatusOK, refs, &meta, nil, nil) 80 } 81 82 // ListNotSynchronizedOn list all directories not synchronized on a device 83 // GET /data/:type/:id/relationships/not_synchronizing 84 // Beware, this is actually used in the web/data Routes 85 func ListNotSynchronizedOn(c echo.Context) error { 86 instance := middlewares.GetInstance(c) 87 doctype := c.Param("doctype") 88 id := getDocID(c) 89 includeDocs := c.QueryParam("include") == "files" 90 91 if err := middlewares.AllowTypeAndID(c, permission.GET, doctype, id); err != nil { 92 if middlewares.AllowWholeType(c, permission.GET, consts.Files) != nil { 93 return err 94 } 95 } 96 97 cursor, err := jsonapi.ExtractPaginationCursor(c, defaultRefsPerPage, maxRefsPerPage) 98 if err != nil { 99 return err 100 } 101 102 start := []string{doctype, id} 103 end := []string{doctype, id, couchdb.MaxString} 104 req := &couchdb.ViewRequest{ 105 StartKey: start, 106 EndKey: end, 107 IncludeDocs: includeDocs, 108 } 109 cursor.ApplyTo(req) 110 111 var res couchdb.ViewResponse 112 if err := couchdb.ExecView(instance, couchdb.DirNotSynchronizedOnView, req, &res); err != nil { 113 return err 114 } 115 116 cursor.UpdateFrom(&res) 117 links := &jsonapi.LinksList{} 118 if cursor.HasMore() { 119 params, err2 := jsonapi.PaginationCursorToParams(cursor) 120 if err2 != nil { 121 return err2 122 } 123 links.Next = fmt.Sprintf("%s?%s", 124 c.Request().URL.Path, params.Encode()) 125 } 126 127 meta := &jsonapi.Meta{Count: &res.Total} 128 129 refs := make([]couchdb.DocReference, len(res.Rows)) 130 var docs []jsonapi.Object 131 if includeDocs { 132 docs = make([]jsonapi.Object, len(res.Rows)) 133 } 134 135 for i, row := range res.Rows { 136 refs[i] = couchdb.DocReference{ 137 ID: row.ID, 138 Type: consts.Files, 139 } 140 141 if includeDocs { 142 docs[i], err = rawMessageToObject(instance, row.Doc) 143 if err != nil { 144 return err 145 } 146 } 147 } 148 149 return jsonapi.DataRelations(c, http.StatusOK, refs, meta, links, docs) 150 } 151 152 // AddBulkNotSynchronizedOn add some not_synchronized_on for a device 153 // POST /data/:type/:id/relationships/not_synchronizing 154 // Beware, this is actually used in the web/data Routes 155 func AddBulkNotSynchronizedOn(c echo.Context) error { 156 instance := middlewares.GetInstance(c) 157 doctype := c.Param("doctype") 158 id := getDocID(c) 159 160 references, err := jsonapi.BindRelations(c.Request()) 161 if err != nil { 162 return WrapVfsError(err) 163 } 164 165 docRef := couchdb.DocReference{ 166 Type: doctype, 167 ID: id, 168 } 169 170 if err = middlewares.AllowTypeAndID(c, permission.PUT, doctype, id); err != nil { 171 if middlewares.AllowWholeType(c, permission.PATCH, consts.Files) != nil { 172 return err 173 } 174 } 175 176 docs := make([]interface{}, len(references)) 177 oldDocs := make([]interface{}, len(references)) 178 179 for i, ref := range references { 180 dir, _, err := instance.VFS().DirOrFileByID(ref.ID) 181 if err != nil { 182 return WrapVfsError(err) 183 } 184 if dir == nil { 185 return jsonapi.BadRequest(errors.New("Cannot add not_synchronized_on on files")) 186 } 187 oldDocs[i] = dir.Clone() 188 dir.AddNotSynchronizedOn(docRef) 189 updateDirCozyMetadata(c, dir) 190 docs[i] = dir 191 } 192 193 // Use bulk update for better performances 194 err = couchdb.BulkUpdateDocs(instance, consts.Files, docs, oldDocs) 195 if err != nil { 196 return WrapVfsError(err) 197 } 198 return c.NoContent(204) 199 } 200 201 // RemoveBulkNotSynchronizedOn removes some not_synchronized_on for several 202 // directories. 203 // DELETE /data/:type/:id/relationships/not_synchronizing 204 // Beware, this is actually used in the web/data Routes 205 func RemoveBulkNotSynchronizedOn(c echo.Context) error { 206 instance := middlewares.GetInstance(c) 207 doctype := c.Param("doctype") 208 id := getDocID(c) 209 210 references, err := jsonapi.BindRelations(c.Request()) 211 if err != nil { 212 return WrapVfsError(err) 213 } 214 215 docRef := couchdb.DocReference{ 216 Type: doctype, 217 ID: id, 218 } 219 220 if err := middlewares.AllowTypeAndID(c, permission.DELETE, doctype, id); err != nil { 221 if middlewares.AllowWholeType(c, permission.PATCH, consts.Files) != nil { 222 return err 223 } 224 } 225 226 docs := make([]interface{}, len(references)) 227 oldDocs := make([]interface{}, len(references)) 228 229 for i, ref := range references { 230 dir, _, err := instance.VFS().DirOrFileByID(ref.ID) 231 if err != nil { 232 return WrapVfsError(err) 233 } 234 if dir == nil { 235 return jsonapi.BadRequest(errors.New("Cannot add not_synchronized_on on files")) 236 } 237 oldDocs[i] = dir.Clone() 238 dir.RemoveNotSynchronizedOn(docRef) 239 updateDirCozyMetadata(c, dir) 240 docs[i] = dir 241 } 242 243 // Use bulk update for better performances 244 err = couchdb.BulkUpdateDocs(instance, consts.Files, docs, oldDocs) 245 if err != nil { 246 return WrapVfsError(err) 247 } 248 return c.NoContent(204) 249 } 250 251 // NotSynchronizedOnRoutes adds the /data/:doctype/:docid/relationships/not_synchronizing routes. 252 func NotSynchronizedOnRoutes(router *echo.Group) { 253 router.GET("/:docid/relationships/not_synchronizing", ListNotSynchronizedOn) 254 router.POST("/:docid/relationships/not_synchronizing", AddBulkNotSynchronizedOn) 255 router.DELETE("/:docid/relationships/not_synchronizing", RemoveBulkNotSynchronizedOn) 256 }