package application import ( "bytes" "context" "encoding/json" "errors" "fmt" "image/png" "io" "log" "net/http" "sort" "strconv" "strings" "time" formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" profilepictures "git.coopgo.io/coopgo-apps/parcoursmob/utils/profile-pictures" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" "git.coopgo.io/coopgo-platform/groups-management/storage" mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" "github.com/google/uuid" "github.com/gorilla/mux" "google.golang.org/protobuf/types/known/structpb" ) type BeneficiariesForm struct { FirstName string `json:"first_name" validate:"required"` LastName string `json:"last_name" validate:"required"` Email string `json:"email" validate:"required,email"` Birthdate *time.Time `json:"birthdate" validate:"required"` PhoneNumber string `json:"phone_number" validate:"required,phoneNumber"` FileNumber string `json:"file_number"` Address any `json:"address,omitempty"` Gender string `json:"gender"` } func (h *ApplicationHandler) BeneficiariesList(w http.ResponseWriter, r *http.Request) { accounts, err := h.beneficiaries(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } sort.Sort(sorting.BeneficiariesByName(accounts)) cacheid := uuid.NewString() h.cache.PutWithTTL(cacheid, accounts, 1*time.Hour) h.Renderer.BeneficiariesList(w, r, accounts, cacheid) } func (h *ApplicationHandler) BeneficiaryCreate(w http.ResponseWriter, r *http.Request) { g := r.Context().Value(identification.GroupKey) if g == nil { fmt.Println("Create beneficiary : could not find group") w.WriteHeader(http.StatusBadRequest) return } group := g.(storage.Group) if r.Method == "POST" { dataMap, err := parseBeneficiariesForm(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } data, err := structpb.NewValue(dataMap) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } request := &mobilityaccounts.RegisterRequest{ Account: &mobilityaccounts.Account{ Namespace: "parcoursmob_beneficiaries", Data: data.GetStructValue(), }, } resp, err := h.services.GRPC.MobilityAccounts.Register(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } subscribe := &groupsmanagement.SubscribeRequest{ Groupid: group.ID, Memberid: resp.Account.Id, } _, err = h.services.GRPC.GroupsManagement.Subscribe(context.TODO(), subscribe) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) return } h.Renderer.BeneficiaryCreate(w, r) } func (h *ApplicationHandler) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] documents := h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiaryID) request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } bookingsrequest := &fleets.GetDriverBookingsRequest{ Driver: beneficiaryID, } bookingsresp, err := h.services.GRPC.Fleets.GetDriverBookings(context.TODO(), bookingsrequest) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } bookings := []any{} for _, b := range bookingsresp.Bookings { bookings = append(bookings, b.ToStorageType()) } groupsrequest := &groupsmanagement.GetGroupsRequest{ Namespaces: []string{"parcoursmob_organizations"}, Member: beneficiaryID, } groupsresp, err := h.services.GRPC.GroupsManagement.GetGroups(context.TODO(), groupsrequest) if err != nil { fmt.Println(err) } organizations := []any{} for _, o := range groupsresp.Groups { organizations = append(organizations, o.ToStorageType()) } beneficiaries_file_types := h.config.GetStringSlice("modules.beneficiaries.documents_types") file_types_map := h.config.GetStringMapString("storage.files.file_types") h.Renderer.BeneficiaryDisplay(w, r, resp.Account.ToStorageType(), bookings, organizations, beneficiaries_file_types, file_types_map, documents) } func (h *ApplicationHandler) BeneficiaryUpdate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] if r.Method == "POST" { dataMap, err := parseBeneficiariesForm(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } data, err := structpb.NewValue(dataMap) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } request := &mobilityaccounts.UpdateDataRequest{ Account: &mobilityaccounts.Account{ Id: beneficiaryID, Namespace: "parcoursmob_beneficiaries", Data: data.GetStructValue(), }, } resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) return } request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } //TODO filter namespaces //TODO filter groups h.Renderer.BeneficiaryUpdate(w, r, resp.Account.ToStorageType()) } func (h *ApplicationHandler) BeneficiaryPicture(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } account := resp.Account.ToStorageType() firstName := account.Data["first_name"].(string) lastName := account.Data["last_name"].(string) picture := profilepictures.DefaultProfilePicture(strings.ToUpper(firstName[0:1] + lastName[0:1])) buffer := new(bytes.Buffer) if err := png.Encode(buffer, picture); err != nil { log.Println("unable to encode image.") } w.Header().Set("Content-Type", "image/png") w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes()))) if _, err := w.Write(buffer.Bytes()); err != nil { log.Println("unable to write image.") } } func (h *ApplicationHandler) BeneficiaryDocuments(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] //r.ParseForm() r.ParseMultipartForm(10 * 1024 * 1024) document_type := r.FormValue("type") document_name := r.FormValue("name") file, header, err := r.FormFile("file-upload") if err != nil { fmt.Println(err) return } defer file.Close() fileid := uuid.NewString() metadata := map[string]string{ "type": document_type, "name": document_name, } if err := h.filestorage.Put(file, filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s_%s", beneficiaryID, fileid, header.Filename), header.Size, metadata); err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) } func (h *ApplicationHandler) BeneficiaryDocumentDownload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] document := vars["document"] file, info, err := h.filestorage.Get(filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s", beneficiaryID, document)) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", info.ContentType) if _, err = io.Copy(w, file); err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) } func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool { searchFilter, ok := r.URL.Query()["search"] if ok && len(searchFilter[0]) > 0 { name := a.Data.AsMap()["first_name"].(string) + " " + a.Data.AsMap()["last_name"].(string) if !strings.Contains(strings.ToLower(name), strings.ToLower(searchFilter[0])) { return false } } return true } func (h *ApplicationHandler) beneficiaries(r *http.Request) ([]mobilityaccountsstorage.Account, error) { var accounts = []mobilityaccountsstorage.Account{} g := r.Context().Value(identification.GroupKey) if g == nil { return accounts, errors.New("no group provided") } group := g.(storage.Group) request := &mobilityaccounts.GetAccountsBatchRequest{ Accountids: group.Members, } resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(context.TODO(), request) if err != nil { return accounts, err } for _, account := range resp.Accounts { if filterAccount(r, account) { a := account.ToStorageType() accounts = append(accounts, a) } } return accounts, err } func parseBeneficiariesForm(r *http.Request) (map[string]any, error) { if err := r.ParseForm(); err != nil { return nil, err } var date *time.Time if r.PostFormValue("birthdate") != "" { d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate")) if err != nil { return nil, err } date = &d } formData := BeneficiariesForm{ FirstName: r.PostFormValue("first_name"), LastName: r.PostFormValue("last_name"), Email: r.PostFormValue("email"), Birthdate: date, PhoneNumber: r.PostFormValue("phone_number"), FileNumber: r.PostFormValue("file_number"), Gender: r.PostFormValue("gender"), } if r.PostFormValue("address") != "" { var a any json.Unmarshal([]byte(r.PostFormValue("address")), &a) formData.Address = a } validate := formvalidators.New() if err := validate.Struct(formData); err != nil { return nil, err } d, err := json.Marshal(formData) if err != nil { return nil, err } var dataMap map[string]any err = json.Unmarshal(d, &dataMap) if err != nil { return nil, err } return dataMap, nil }