package main import ( "encoding/json" "fmt" "io" "log" "net/http" "path/filepath" "regexp" "strings" "time" "github.com/gorilla/mux" ) func MPE_IsProject(input *Payload) (projectnumber string, project_name string) { // this will attempt to extract a project number from the "targetpath" element. If it's able to determine a folder contains a project number, we'll return it. // var path_data map[string]json.RawMessage project_regex, _ := regexp.Compile(`(^[0-9]{1,3})`) // regex to match against // err := json.Unmarshal((input.Data), &path_data) // if err != nil { // log.Fatal(err) // fmt.Println("oops") // return // } path := string(input.Data[0].PathData.TargetPath) split_path, _ := filepath.Split(path) split_path = filepath.Clean(split_path) separated_path := strings.Split(split_path, "/") //turn this into an array (slice?) to parse var sb strings.Builder // iterate through the path_data map. We want to find all strings that start with a number. for keyvalue, value := range separated_path { matched := project_regex.MatchString(value) // Possible Fix for Project 3rd Number ID. var ProjectSection bool // <0>/shared<1>/N-Data<2>/<3>/<4>/<5>/<6>/<7> switch keyvalue { case 0: ProjectSection = false // "blank" case 1: ProjectSection = false // shared case 2: ProjectSection = false // N-Data case 3: ProjectSection = true // 2 digit case 4: ProjectSection = true // 2 digit client name case 5: ProjectSection = true // Project Number case 6: ProjectSection = true // Phase } if matched && ProjectSection { // if we match the Regex above, write values to SB. This will give us a string like "73|10 Office Of Somone|153 HelpMe|002 Phase 2" sb.WriteString(value + "|") // add pipe separator } else { // DoNothing } } // end of loop // now to assemble project number and project name from the resulting values... project_text := sb.String() project_slice := strings.Split(project_text, "|") if strings.Count(project_text, "|") > 2 { // if more than 2 pipe | chars in string, could be a project. // first value should be a digit, if not this can't be a project isProject0, err := regexp.MatchString(`(^[0-9])`, project_slice[0]) if err != nil { fmt.Printf("err.Error(): %v\n", err.Error()) } isProject1, err := regexp.MatchString(`(^[0-9])`, project_slice[1]) if err != nil { fmt.Printf("err.Error(): %v\n", err.Error()) } isProject2, err := regexp.MatchString(`(^[0-9])`, project_slice[2]) if err != nil { fmt.Printf("err.Error(): %v\n", err.Error()) } // isProject, _ = regexp.MatchString(`(^[0-9])`, project_slice[3]) if isProject0 && isProject1 && isProject2 { project_regex, _ := regexp.Compile(`(^[0-9]{1,3})`) // regex to match against regex_tidy_string, _ := regexp.Compile(`[-_]+|^\s`) //regex to tidy up folder paths client_section := regex_tidy_string.ReplaceAllString(project_slice[1], "") // remove fancy chars from Client Name project_section := regex_tidy_string.ReplaceAllString(project_regex.ReplaceAllString(project_slice[2], ""), " ") // remove leading-digits project_section_digits := strings.Split(regex_tidy_string.ReplaceAllString(project_slice[2], " "), " ") //split on space for MOST file folders... sb.Reset() // reset string builder so I can use it again! sb.WriteString(project_slice[0]) // 73 sb.WriteString(strings.Split(client_section, " ")[0] + "-") // 7310 -- Client Name sb.WriteString(project_section_digits[0]) // 7310-10 // do we have a third section of the project number? project_third := strings.Split(project_slice[3], " ")[0] project_third_match, _ := regexp.MatchString(`(^[0-9]{1,3})`, project_third) /* TODO: Better identification of the third number. Currently seeing paths such as "/shared/n-data/12/34 test/45 ProjectName/deliverables/05 geotech" generating project numbers of 1234-45-05, where it should be 1234-45 This is due to the RegEx Match looking for all folders that start with digits, and not being concerned about where they are in the string. */ if project_third_match { // if we have a third section, then add another section to the project number, and append to the project name third_project_name := project_regex.ReplaceAllString(project_slice[3], "") // 7310-10-153 project_name = project_section + "-" + regex_tidy_string.ReplaceAllString(third_project_name, "") // remove project digits from beginning of string and append project_third = regex_tidy_string.ReplaceAllString(project_third, " ") sb.WriteString("-" + project_third) } else { project_name = project_section // remove project digits from beginning of string } projectnumber = sb.String() // I should now have a project number! } else { return "NotAProject", "NoPath" } } return } func MPE_ValidateForward(in *Payload) bool { // decode targetPath data. Will return TRUE if the last section of the path contains "External Share" or "ExternalShare". This would indicate that we SHOULD forward this request onward. // var path_data map[string]json.RawMessage // err := json.Unmarshal((in.Data), &path_data) // if err != nil { // log.Fatal(err) // fmt.Println("oops") // return false // } path := string(in.Data[0].PathData.TargetPath) split_path, last := filepath.Split(path) split_path = filepath.Clean(split_path) matched, _ := regexp.MatchString(`ExternalShare|External Share`, last) return matched } func readDataStream(resp http.ResponseWriter, request *http.Request) { // reqBody, _ := io.ReadAll(request.Body) colorReset := "\033[0m" // colorRed := "\033[31m" colorGreen := "\033[32m" colorYellow := "\033[33m" colorBlue := "\033[34m" // colorPurple := "\033[35m" // colorCyan := "\033[36m" // colorWhite := "\033[37m" var log_values log_data var request_body Payload // init request_body as data type Payload - uses custom structs body_string, _ := io.ReadAll(request.Body) // ready Body Data log_values.BodyData = string(body_string) // prep for logging // fmt.Printf("%+v\n", log_values) // debug code to show JASON data coming in err := json.Unmarshal(body_string, &request_body) if err != nil { var error_sb strings.Builder error_sb.WriteString("ERROR in JSON\n") error_sb.WriteString(string(body_string)) error_sb.WriteString("\n--Error from Unmarshal: ") error_sb.WriteString(err.Error()) fmt.Print(error_sb.String()) resp.WriteHeader(http.StatusBadRequest) resp.Write([]byte(error_sb.String())) return } log_values.EventID = request_body.Data[0].EventID log_values.EventType = request_body.Data[0].EventType log_values.User = request_body.Data[0].User.DisplayName log_values.TargetPath = colorGreen + request_body.Data[0].PathData.TargetPath + colorReset // fmt.Printf("%+v\n", log_values) // output log values to stdout - first round var MPE_ShouldFoward = MPE_ValidateForward(&request_body) var MPE_ProjectNumber, MPE_ProjectName = MPE_IsProject(&request_body) log_values.ExternalShare = MPE_ShouldFoward log_values.MPE_ProjectName = colorYellow + MPE_ProjectName + colorReset log_values.MPE_ProjectNumber = colorBlue + MPE_ProjectNumber + colorReset fmt.Printf("%+v\n", log_values) // output log values to stdout - second round // Collect EventID, JSON Content, and return values into data object to be logged to screen. Validate data visually for a bit against "live" webhook data. // TODO: Once Data validation / Struct is built, pass data off to next Webhook URL for processing. } func HealthCheck(resp http.ResponseWriter, request *http.Request) { var t = time.Now().String() var MessageText = t + ": Health Check from " + request.RemoteAddr // reqBody, _ := io.ReadAll(request.Body) fmt.Println(http.StatusOK, MessageText) resp.WriteHeader(http.StatusOK) resp.Write([]byte(MessageText)) } func CatchError(resp http.ResponseWriter, request *http.Request) { var LogText = "got bad request from " + request.RemoteAddr + ". Method: " + request.Method fmt.Println(http.StatusBadRequest, LogText) var MessageText = "Invalid Request, please read the docs..." resp.WriteHeader(http.StatusBadRequest) resp.Write([]byte(MessageText)) } func handleRequests() { // Start new Mux router mainrouter := mux.NewRouter().StrictSlash(true) mainrouter.HandleFunc("/", readDataStream).Methods("POST") mainrouter.HandleFunc("/healthcheck", HealthCheck).Methods("GET") mainrouter.HandleFunc("/", CatchError) log.Fatal(http.ListenAndServe(":10000", mainrouter)) } func main() { var t = time.Now().String() fmt.Println("MPE x Egnyte x PowerApps - Mux Router starting: " + t) handleRequests() } type Payload struct { Data []EG_Data `json:"data"` } type User struct { ID int `json:"id"` ClientIDHash string `json:"clientIdHash"` DisplayName string `json:"displayName"` Username string `json:"username"` Email string `json:"email"` ImpersonatedBy string `json:"impersonatedBy"` } type PathData struct { SourcePath string `json:"sourcePath"` TargetPath string `json:"targetPath"` } type EG_Data struct { EventID string `json:"eventId"` Domain string `json:"domain"` Timestamp int64 `json:"timestamp"` User User `json:"user"` ActionSource string `json:"actionSource"` WebhookID string `json:"webhookId"` EventType string `json:"eventType"` PathData PathData `json:"data"` } type log_data struct { EventID string User string EventType string TargetPath string ExternalShare bool MPE_ProjectNumber string MPE_ProjectName string BodyData string }