github.com/minio/console@v1.4.1/api/user_watch.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 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 <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 "regexp" 25 "strings" 26 27 mc "github.com/minio/mc/cmd" 28 "github.com/minio/websocket" 29 ) 30 31 type watchOptions struct { 32 BucketName string 33 mc.WatchOptions 34 } 35 36 func startWatch(ctx context.Context, conn WSConn, wsc MCClient, options *watchOptions) error { 37 wo, pErr := wsc.watch(ctx, options.WatchOptions) 38 if pErr != nil { 39 LogError("error initializing watch: %v", pErr.Cause) 40 return pErr.Cause 41 } 42 for { 43 select { 44 case <-ctx.Done(): 45 close(wo.DoneChan) 46 return nil 47 case events, ok := <-wo.Events(): 48 // zero value returned because the channel is closed and empty 49 if !ok { 50 return nil 51 } 52 for _, event := range events { 53 // Serialize message to be sent 54 bytes, err := json.Marshal(event) 55 if err != nil { 56 LogError("error on json.Marshal: %v", err) 57 return err 58 } 59 // Send Message through websocket connection 60 err = conn.writeMessage(websocket.TextMessage, bytes) 61 if err != nil { 62 LogError("error writeMessage: %v", err) 63 return err 64 } 65 } 66 case pErr, ok := <-wo.Errors(): 67 // zero value returned because the channel is closed and empty 68 if !ok { 69 return nil 70 } 71 if pErr != nil { 72 LogError("error on watch: %v", pErr.Cause) 73 return pErr.Cause 74 75 } 76 } 77 } 78 } 79 80 // getWatchOptionsFromReq gets bucket name, events, prefix, suffix from a websocket 81 // watch path if defined. 82 // path come as : `/watch/bucket1` and query 83 // params come on request form 84 func getWatchOptionsFromReq(req *http.Request) (*watchOptions, error) { 85 wOptions := watchOptions{} 86 // Default Events if not defined 87 wOptions.Events = []string{"put", "get", "delete"} 88 89 re := regexp.MustCompile(`(/watch/)(.*?$)`) 90 matches := re.FindAllSubmatch([]byte(req.URL.Path), -1) 91 // matches comes as e.g. 92 // [["...", "/watch/", "bucket1"]] 93 // [["/watch/" "/watch/" ""]] 94 95 if len(matches) == 0 || len(matches[0]) < 3 { 96 return nil, fmt.Errorf("invalid url: %s", req.URL.Path) 97 } 98 99 wOptions.BucketName = strings.TrimSpace(string(matches[0][2])) 100 101 events := req.FormValue("events") 102 if strings.TrimSpace(events) != "" { 103 wOptions.Events = strings.Split(events, ",") 104 } 105 wOptions.Prefix = req.FormValue("prefix") 106 wOptions.Suffix = req.FormValue("suffix") 107 return &wOptions, nil 108 }