diff --git a/go.mod b/go.mod index 9ac3353..35027da 100644 --- a/go.mod +++ b/go.mod @@ -2,22 +2,17 @@ module git.coopgo.io/coopgo-apps/parcoursmob go 1.18 -replace git.coopgo.io/coopgo-platform/mobility-accounts => ../../coopgo-platform/mobility-accounts/ +// replace git.coopgo.io/coopgo-platform/mobility-accounts => ../../coopgo-platform/mobility-accounts/ -replace git.coopgo.io/coopgo-platform/groups-management => ../../coopgo-platform/groups-management/ +// replace git.coopgo.io/coopgo-platform/groups-management => ../../coopgo-platform/groups-management/ -replace git.coopgo.io/coopgo-platform/fleets => ../../coopgo-platform/fleets/ +// replace git.coopgo.io/coopgo-platform/fleets => ../../coopgo-platform/fleets/ -replace git.coopgo.io/coopgo-platform/agenda => ../../coopgo-platform/agenda/ +// replace git.coopgo.io/coopgo-platform/agenda => ../../coopgo-platform/agenda/ -replace git.coopgo.io/coopgo-platform/emailing => ../../coopgo-platform/emailing/ +// replace git.coopgo.io/coopgo-platform/emailing => ../../coopgo-platform/emailing/ require ( - git.coopgo.io/coopgo-platform/agenda v0.0.0-00010101000000-000000000000 - git.coopgo.io/coopgo-platform/emailing v0.0.0-00010101000000-000000000000 - git.coopgo.io/coopgo-platform/fleets v0.0.0-00010101000000-000000000000 - git.coopgo.io/coopgo-platform/groups-management v0.0.0-00010101000000-000000000000 - git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-00010101000000-000000000000 github.com/coreos/go-oidc v2.2.1+incompatible github.com/fogleman/gg v1.3.0 github.com/go-playground/validator/v10 v10.11.0 @@ -34,6 +29,14 @@ require ( google.golang.org/protobuf v1.28.1 ) +require ( + git.coopgo.io/coopgo-platform/agenda v0.0.0-20221017030035-4a26fc791c5b + git.coopgo.io/coopgo-platform/emailing v0.0.0-20221017030337-c71888d90c15 + git.coopgo.io/coopgo-platform/fleets v0.0.0-20220905052643-be9ee8372fdd + git.coopgo.io/coopgo-platform/groups-management v0.0.0-20221017025751-671dc9a2c544 + git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20220906130339-b9a32e41bffe +) + require ( github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect diff --git a/go.sum b/go.sum index f27538e..68c1ff4 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +git.coopgo.io/coopgo-platform/agenda v0.0.0-20221017030035-4a26fc791c5b h1:7kLW1khfGguZ2aL+QpWFwZmAdEcY1MsUjLdiRufjr2s= +git.coopgo.io/coopgo-platform/agenda v0.0.0-20221017030035-4a26fc791c5b/go.mod h1:wqPvfYmzGF2cfXbs8XE1P2j5UYqZwp/La0llkl7dUkc= +git.coopgo.io/coopgo-platform/emailing v0.0.0-20221017030337-c71888d90c15 h1:+ZI4nGE6mqZ6pc7N/BizheEPRXn6Z84Sj7ikwfP2ZcU= +git.coopgo.io/coopgo-platform/emailing v0.0.0-20221017030337-c71888d90c15/go.mod h1:rmbqiHVkONcECOoPlsXlxZnD315Tiz2oRnn1M7646Kg= +git.coopgo.io/coopgo-platform/fleets v0.0.0-20220905052643-be9ee8372fdd h1:7k5QMwMm6JQ0S2bNqXEe7Ouh8N9N3yAvcWB2GRcIZLk= +git.coopgo.io/coopgo-platform/fleets v0.0.0-20220905052643-be9ee8372fdd/go.mod h1:s9OIFCNcjBAbBzRNHwoCTYV6kAntPG9CpT3GVweGdTY= +git.coopgo.io/coopgo-platform/groups-management v0.0.0-20221017025751-671dc9a2c544 h1:rMLP77uIEequVXXZ0X9G1iK2k+xvW/+58ggwxxI6gqY= +git.coopgo.io/coopgo-platform/groups-management v0.0.0-20221017025751-671dc9a2c544/go.mod h1:lozSy6qlIIYhvKKXscZzz28HAtS0qBDUTv5nofLRmYA= +git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20220906130339-b9a32e41bffe h1:4OKwfKybR0VsIw2dSM9RtqGWveWPt+JjtiiMIBrg/w0= +git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20220906130339-b9a32e41bffe/go.mod h1:1typNYtO+PQT6KG77vs/PUv0fO60/nbeSGZL2tt1LLg= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/handlers/api/api.go b/handlers/api/api.go index f21d85c..785db34 100644 --- a/handlers/api/api.go +++ b/handlers/api/api.go @@ -4,8 +4,8 @@ import ( "net/http" "git.coopgo.io/coopgo-apps/parcoursmob/services" - "git.coopgo.io/coopgo-apps/parcoursmob/utils/cache" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" "github.com/spf13/viper" ) @@ -13,10 +13,10 @@ type APIHandler struct { idp *identification.IdentificationProvider config *viper.Viper services *services.ServicesHandler - cache *cache.CacheHandler + cache cache.CacheHandler } -func NewAPIHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache *cache.CacheHandler) (*APIHandler, error) { +func NewAPIHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache cache.CacheHandler) (*APIHandler, error) { return &APIHandler{ idp: idp, config: cfg, diff --git a/handlers/api/export.go b/handlers/api/export.go new file mode 100644 index 0000000..2b343ff --- /dev/null +++ b/handlers/api/export.go @@ -0,0 +1,101 @@ +package api + +import ( + "encoding/csv" + "fmt" + "net/http" + "sort" + "strconv" + + "github.com/gorilla/mux" +) + +type FlatMaps []map[string]any + +func (maps FlatMaps) GetHeaders() (res []string) { + keys := map[string]bool{} + for _, m := range maps { + for k, _ := range m { + if _, ok := keys[k]; !ok { + keys[k] = true + res = append(res, k) + } + } + } + sort.Strings(res) + return +} + +func (maps FlatMaps) GetValues() (res [][]string) { + headers := maps.GetHeaders() + for _, m := range maps { + line := []string{} + for _, k := range headers { + if v, ok := m[k]; ok && v != nil { + line = append(line, fmt.Sprint(v)) + } else { + line = append(line, "") + } + } + res = append(res, line) + } + return +} + +func (h APIHandler) CacheExport(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + cacheid := vars["cacheid"] + + d, err := h.cache.Get(cacheid) + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusNotFound) + return + } + + if data, ok := d.([]any); ok { + + flatmaps := FlatMaps{} + //fmt.Println(data) + + for _, v := range data { + fmt.Println(v) + fm := map[string]any{} + flatten("", v.(map[string]any), fm) + fmt.Println(fm) + flatmaps = append(flatmaps, fm) + } + + fmt.Println(flatmaps) + + w.Header().Set("Content-Type", "text/csv") + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=export-%s.csv", cacheid)) + c := csv.NewWriter(w) + c.Write(flatmaps.GetHeaders()) + c.WriteAll(flatmaps.GetValues()) + return + + } + + w.WriteHeader(http.StatusNotFound) + +} + +func flatten(prefix string, src map[string]any, dest map[string]any) { + if len(prefix) > 0 { + prefix += "." + } + for k, v := range src { + switch child := v.(type) { + case map[string]any: + flatten(prefix+k, child, dest) + case []any: + for i := 0; i < len(child); i++ { + dest[prefix+k+"."+strconv.Itoa(i)] = child[i] + } + default: + fmt.Println(prefix+k, " : ", v) + dest[prefix+k] = v + } + } +} diff --git a/handlers/api/oidc.go b/handlers/api/oidc.go index f0e43cc..b517129 100644 --- a/handlers/api/oidc.go +++ b/handlers/api/oidc.go @@ -18,6 +18,7 @@ func (h APIHandler) OAuth2Callback(w http.ResponseWriter, r *http.Request) { // Extract the ID Token from OAuth2 token. rawIDToken, ok := oauth2Token.Extra("id_token").(string) if !ok { + fmt.Println("issue retrieving token") w.WriteHeader(http.StatusInternalServerError) return } @@ -36,11 +37,20 @@ func (h APIHandler) OAuth2Callback(w http.ResponseWriter, r *http.Request) { redirect := "/app/" if session.Values["redirect"] != nil && session.Values["redirect"] != "" { + fmt.Println("no redirect stuff") redirect = session.Values["redirect"].(string) delete(session.Values, "redirect") } - session.Save(r, w) + if err = session.Save(r, w); err != nil { + fmt.Println(err) + panic(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + fmt.Println(session.Values) + fmt.Println("redirect") http.Redirect(w, r, redirect, http.StatusFound) } diff --git a/handlers/application/administration.go b/handlers/application/administration.go index d8cb20a..9abb126 100644 --- a/handlers/application/administration.go +++ b/handlers/application/administration.go @@ -229,7 +229,7 @@ func (h *ApplicationHandler) AdministrationGroupInviteAdmin(w http.ResponseWrite } key := base64.RawURLEncoding.EncodeToString(b) - h.cache.PutWithTTL("onboarding/"+key, onboarding, 72*time.Hour) + h.cache.PutWithTTL("onboarding/"+key, onboarding, 168*time.Hour) // 1 week TTL data := map[string]any{ "group": groupresp.Group.ToStorageType().Data["name"], @@ -247,6 +247,89 @@ func (h *ApplicationHandler) AdministrationGroupInviteAdmin(w http.ResponseWrite return } +func (h *ApplicationHandler) AdministrationGroupInviteMember(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + groupid := vars["groupid"] + + groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(context.TODO(), &groupsmanagement.GetGroupRequest{ + Id: groupid, + Namespace: "parcoursmob_organizations", + }) + + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + group := groupresp.Group.ToStorageType() + + r.ParseForm() + + accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountUsername(context.TODO(), &accounts.GetAccountUsernameRequest{ + Username: r.FormValue("username"), + Namespace: "parcoursmob", + }) + + if err == nil { + account := accountresp.Account.ToStorageType() + account.Data["groups"] = append(account.Data["groups"].([]any), group.ID) + + as, _ := accounts.AccountFromStorageType(&account) + + _, err = h.services.GRPC.MobilityAccounts.UpdateData( + context.TODO(), + &accounts.UpdateDataRequest{ + Account: as, + }, + ) + + fmt.Println(err) + + data := map[string]any{ + "group": group.Data["name"], + } + + if err := h.emailing.Send("onboarding.existing_member", r.FormValue("username"), data); err != nil { + fmt.Println(err) + } + + http.Redirect(w, r, "/app/group/settings", http.StatusFound) + return + } else { + // Onboard now administrator + onboarding := map[string]any{ + "username": r.FormValue("username"), + "group": group.ID, + "admin": false, + } + + b := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, b); err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + key := base64.RawURLEncoding.EncodeToString(b) + + h.cache.PutWithTTL("onboarding/"+key, onboarding, 168*time.Hour) // 1 week TTL + + data := map[string]any{ + "group": group.Data["name"], + "key": key, + } + + if err := h.emailing.Send("onboarding.new_member", r.FormValue("username"), data); err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + } + + http.Redirect(w, r, "/app/administration/groups/"+group.ID, http.StatusFound) + return +} + func (h *ApplicationHandler) members() ([]*accounts.Account, error) { resp, err := h.services.GRPC.MobilityAccounts.GetAccounts(context.TODO(), &accounts.GetAccountsRequest{ Namespaces: []string{"parcoursmob"}, diff --git a/handlers/application/application.go b/handlers/application/application.go index ea7e75a..64c402c 100644 --- a/handlers/application/application.go +++ b/handlers/application/application.go @@ -5,7 +5,7 @@ import ( "git.coopgo.io/coopgo-apps/parcoursmob/renderer" "git.coopgo.io/coopgo-apps/parcoursmob/services" - "git.coopgo.io/coopgo-apps/parcoursmob/utils/cache" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" "git.coopgo.io/coopgo-platform/emailing" "github.com/spf13/viper" ) @@ -14,11 +14,11 @@ type ApplicationHandler struct { config *viper.Viper Renderer *renderer.Renderer services *services.ServicesHandler - cache *cache.CacheHandler + cache cache.CacheHandler emailing *emailing.Mailer } -func NewApplicationHandler(cfg *viper.Viper, svc *services.ServicesHandler, cache *cache.CacheHandler, emailing *emailing.Mailer) (*ApplicationHandler, error) { +func NewApplicationHandler(cfg *viper.Viper, svc *services.ServicesHandler, cache cache.CacheHandler, emailing *emailing.Mailer) (*ApplicationHandler, error) { templates_root := cfg.GetString("templates.root") renderer := renderer.NewRenderer(cfg, templates_root) return &ApplicationHandler{ diff --git a/handlers/application/beneficiaries.go b/handlers/application/beneficiaries.go index a0f9231..eb77f14 100644 --- a/handlers/application/beneficiaries.go +++ b/handlers/application/beneficiaries.go @@ -9,6 +9,7 @@ import ( "image/png" "log" "net/http" + "sort" "strconv" "strings" "time" @@ -20,6 +21,7 @@ import ( 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" @@ -29,12 +31,21 @@ 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"` + Birthdate *time.Time `json:"birthdate" validate:"required"` PhoneNumber string `json:"phone_number" validate:"required,phoneNumber"` + FileNumber string `json:"file_number" validate:"required"` Address any `json:"address,omitempty"` Gender string `json:"gender"` } +type BeneficiariesByName []mobilityaccountsstorage.Account + +func (e BeneficiariesByName) Len() int { return len(e) } +func (e BeneficiariesByName) Less(i, j int) bool { + return e[i].Data["first_name"].(string) < e[j].Data["first_name"].(string) +} +func (e BeneficiariesByName) Swap(i, j int) { e[i], e[j] = e[j], e[i] } + func (h *ApplicationHandler) BeneficiariesList(w http.ResponseWriter, r *http.Request) { accounts, err := h.beneficiaries(r) @@ -44,6 +55,8 @@ func (h *ApplicationHandler) BeneficiariesList(w http.ResponseWriter, r *http.Re return } + sort.Sort(BeneficiariesByName(accounts)) + cacheid := uuid.NewString() h.cache.PutWithTTL(cacheid, accounts, 1*time.Hour) @@ -252,8 +265,8 @@ func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool { return true } -func (h *ApplicationHandler) beneficiaries(r *http.Request) ([]any, error) { - var accounts = []any{} +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") @@ -301,6 +314,7 @@ func parseBeneficiariesForm(r *http.Request) (map[string]any, error) { Email: r.PostFormValue("email"), Birthdate: date, PhoneNumber: r.PostFormValue("phone_number"), + FileNumber: r.PostFormValue("file_number"), Gender: r.PostFormValue("gender"), } diff --git a/handlers/application/journeys.go b/handlers/application/journeys.go index 7fe7d92..04d3ee4 100644 --- a/handlers/application/journeys.go +++ b/handlers/application/journeys.go @@ -81,8 +81,8 @@ func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Reque journeys, err = session.Journeys(context.Background(), request) if err != nil { fmt.Println(err) - w.WriteHeader(http.StatusBadRequest) - return + // w.WriteHeader(http.StatusBadRequest) + // return } //CARPOOL @@ -93,8 +93,6 @@ func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Reque // departuredatetime.Format("2006-01-02"), departuredatetime.Add(24*time.Hour).Format("2006-01-02")) carpoolrequest := "https://api.rdex.ridygo.fr/journeys.json" - fmt.Println(carpoolrequest) - client := &http.Client{} req, err := http.NewRequest("GET", carpoolrequest, nil) if err != nil { @@ -113,11 +111,21 @@ func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Reque fmt.Println(err) } - err = json.NewDecoder(resp.Body).Decode(&carpoolresults) - if err != nil { - fmt.Println(err) + if err == nil && resp.StatusCode == http.StatusOK { + err = json.NewDecoder(resp.Body).Decode(&carpoolresults) + if err != nil { + fmt.Println(err) + } + + if carpoolresults == nil { + carpoolresults = []any{} + } + } else { + carpoolresults = []any{} } + fmt.Println(carpoolresults) + // Vehicles vehiclerequest := &fleets.GetVehiclesRequest{ diff --git a/handlers/auth/auth.go b/handlers/auth/auth.go index 9267be9..43d42bd 100644 --- a/handlers/auth/auth.go +++ b/handlers/auth/auth.go @@ -3,8 +3,9 @@ package auth import ( "git.coopgo.io/coopgo-apps/parcoursmob/renderer" "git.coopgo.io/coopgo-apps/parcoursmob/services" - "git.coopgo.io/coopgo-apps/parcoursmob/utils/cache" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + "git.coopgo.io/coopgo-platform/emailing" "github.com/spf13/viper" ) @@ -13,10 +14,11 @@ type AuthHandler struct { config *viper.Viper services *services.ServicesHandler Renderer *renderer.Renderer - cache *cache.CacheHandler + cache cache.CacheHandler + emailing *emailing.Mailer } -func NewAuthHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache *cache.CacheHandler) (*AuthHandler, error) { +func NewAuthHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache cache.CacheHandler, emailing *emailing.Mailer) (*AuthHandler, error) { templates_root := cfg.GetString("templates.root") renderer := renderer.NewRenderer(cfg, templates_root) return &AuthHandler{ @@ -25,5 +27,6 @@ func NewAuthHandler(cfg *viper.Viper, idp *identification.IdentificationProvider services: svc, Renderer: renderer, cache: cache, + emailing: emailing, }, nil } diff --git a/handlers/auth/lost_password.go b/handlers/auth/lost_password.go new file mode 100644 index 0000000..9647513 --- /dev/null +++ b/handlers/auth/lost_password.go @@ -0,0 +1,97 @@ +package auth + +import ( + "context" + "crypto/rand" + "encoding/base64" + "fmt" + "io" + "net/http" + "time" + + "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" +) + +func (h *AuthHandler) LostPasswordInit(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + r.ParseForm() + email := r.FormValue("email") + if email != "" { + account, err := h.services.GRPC.MobilityAccounts.GetAccountUsername(context.TODO(), &grpcapi.GetAccountUsernameRequest{ + Username: email, + Namespace: "parcoursmob", + }) + if err != nil { + fmt.Println(err) + http.Redirect(w, r, "/app/", http.StatusFound) + return + } + + b := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, b); err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + key := base64.RawURLEncoding.EncodeToString(b) + + passwordretrieval := map[string]any{ + "username": email, + "account_id": account.Account.Id, + "key": key, + } + + h.cache.PutWithTTL("retrieve-password/"+key, passwordretrieval, 72*time.Hour) + + if err := h.emailing.Send("auth.retrieve_password", email, passwordretrieval); err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/app/", http.StatusFound) + } + } + h.Renderer.LostPasswordInit(w, r) + +} + +func (h *AuthHandler) LostPasswordRecover(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + + key := r.FormValue("key") + recover, err := h.cache.Get("retrieve-password/" + key) + if err != nil { + fmt.Println(err) + h.Renderer.LostPasswordRecoverKO(w, r, key) + return + } + + if r.Method == "POST" { + newpassword := r.FormValue("password") + if newpassword == "" { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("Password is empty")) + return + } + + _, err := h.services.GRPC.MobilityAccounts.ChangePassword(context.TODO(), &grpcapi.ChangePasswordRequest{ + Id: recover.(map[string]any)["account_id"].(string), + Password: newpassword, + }) + + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + } + + err = h.cache.Delete("retrieve-password/" + key) + if err != nil { + fmt.Println(err) + } + + http.Redirect(w, r, "/app/", http.StatusFound) + + } + h.Renderer.LostPasswordRecover(w, r, recover) +} diff --git a/handlers/auth/onboarding.go b/handlers/auth/onboarding.go index 15f7287..f1c87a5 100644 --- a/handlers/auth/onboarding.go +++ b/handlers/auth/onboarding.go @@ -16,7 +16,7 @@ func (h *AuthHandler) Onboarding(w http.ResponseWriter, r *http.Request) { onboarding, err := h.cache.Get("onboarding/" + key) if err != nil { fmt.Println(err) - w.WriteHeader(http.StatusInternalServerError) + h.Renderer.AuthOnboardingKO(w, r, key) return } @@ -72,6 +72,12 @@ func (h *AuthHandler) Onboarding(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } + + err = h.cache.Delete("onboarding/" + key) + if err != nil { + fmt.Println(err) + } + http.Redirect(w, r, "/app/", http.StatusFound) } diff --git a/main.go b/main.go index 5f4d0fc..56cffec 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,8 @@ import ( "git.coopgo.io/coopgo-apps/parcoursmob/handlers/auth" "git.coopgo.io/coopgo-apps/parcoursmob/renderer" "git.coopgo.io/coopgo-apps/parcoursmob/services" - "git.coopgo.io/coopgo-apps/parcoursmob/utils/cache" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" "github.com/gorilla/mux" ) @@ -33,12 +33,12 @@ func main() { panic(err) } - idp, err := identification.NewIdentificationProvider(cfg, svc) + kv, err := cache.NewKVHandler(cfg) if err != nil { panic(err) } - cache, err := cache.NewCacheHandler(cfg) + idp, err := identification.NewIdentificationProvider(cfg, svc, kv) if err != nil { panic(err) } @@ -48,9 +48,9 @@ func main() { panic(err) } - apiHandler, _ := api.NewAPIHandler(cfg, idp, svc, cache) - applicationHandler, _ := application.NewApplicationHandler(cfg, svc, cache, emailing) - authHandler, _ := auth.NewAuthHandler(cfg, idp, svc, cache) + apiHandler, _ := api.NewAPIHandler(cfg, idp, svc, kv) + applicationHandler, _ := application.NewApplicationHandler(cfg, svc, kv, emailing) + authHandler, _ := auth.NewAuthHandler(cfg, idp, svc, kv, emailing) fmt.Println("Running", service_name, ":") @@ -59,6 +59,8 @@ func main() { r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir(templates_public_dir)))) r.HandleFunc("/auth/onboarding", authHandler.Onboarding) + r.HandleFunc("/auth/lost-password", authHandler.LostPasswordInit) + r.HandleFunc("/auth/lost-password/recover", authHandler.LostPasswordRecover) r.HandleFunc("/auth/groups/", authHandler.Groups) r.HandleFunc("/auth/groups/switch", authHandler.GroupSwitch) r.HandleFunc("/", redirectApp) @@ -67,6 +69,7 @@ func main() { api_router.HandleFunc("/", apiHandler.NotFound) api_router.HandleFunc("/geo/autocomplete", apiHandler.GeoAutocomplete) api_router.HandleFunc("/cache/{cacheid}", apiHandler.GetCache) + api_router.HandleFunc("/cache/{cacheid}/export", apiHandler.CacheExport) api_router.HandleFunc("/oauth2/callback", apiHandler.OAuth2Callback) application := r.PathPrefix("/app").Subrouter() @@ -104,6 +107,7 @@ func main() { appAdmin.HandleFunc("/groups/", applicationHandler.AdministrationCreateGroup) appAdmin.HandleFunc("/groups/{groupid}", applicationHandler.AdministrationGroupDisplay) appAdmin.HandleFunc("/groups/{groupid}/invite-admin", applicationHandler.AdministrationGroupInviteAdmin) + appAdmin.HandleFunc("/groups/{groupid}/invite-member", applicationHandler.AdministrationGroupInviteMember) //TODO Secure with Middleware checking for modules fmt.Println("-> HTTP server listening on", address) diff --git a/renderer/auth.go b/renderer/auth.go index ca4bd01..957359e 100644 --- a/renderer/auth.go +++ b/renderer/auth.go @@ -13,7 +13,7 @@ func (renderer *Renderer) AuthGroups(w http.ResponseWriter, r *http.Request, gro } func (renderer *Renderer) AuthOnboarding(w http.ResponseWriter, r *http.Request, key string, onboarding any) { - files := renderer.ThemeConfig.GetStringSlice("views.auth.onboarding.files") + files := renderer.ThemeConfig.GetStringSlice("views.auth.onboarding.form.files") state := NewState(r, renderer.ThemeConfig, "") state.ViewState = map[string]any{ "key": key, @@ -22,3 +22,41 @@ func (renderer *Renderer) AuthOnboarding(w http.ResponseWriter, r *http.Request, renderer.RenderNoLayout("onboarding", w, r, files, state) } + +func (renderer *Renderer) AuthOnboardingKO(w http.ResponseWriter, r *http.Request, key string) { + files := renderer.ThemeConfig.GetStringSlice("views.auth.onboarding.ko.files") + state := NewState(r, renderer.ThemeConfig, "") + state.ViewState = map[string]any{ + "key": key, + } + + renderer.RenderNoLayout("onboarding", w, r, files, state) +} + +func (renderer *Renderer) LostPasswordInit(w http.ResponseWriter, r *http.Request) { + files := renderer.ThemeConfig.GetStringSlice("views.auth.lost_password.init.files") + state := NewState(r, renderer.ThemeConfig, "") + state.ViewState = map[string]any{} + + renderer.RenderNoLayout("lost_password_init", w, r, files, state) +} + +func (renderer *Renderer) LostPasswordRecover(w http.ResponseWriter, r *http.Request, recover any) { + files := renderer.ThemeConfig.GetStringSlice("views.auth.lost_password.recover.form.files") + state := NewState(r, renderer.ThemeConfig, "") + state.ViewState = map[string]any{ + "recover": recover, + } + + renderer.RenderNoLayout("lost_password_recover", w, r, files, state) +} + +func (renderer *Renderer) LostPasswordRecoverKO(w http.ResponseWriter, r *http.Request, key string) { + files := renderer.ThemeConfig.GetStringSlice("views.auth.lost_password.recover.ko.files") + state := NewState(r, renderer.ThemeConfig, "") + state.ViewState = map[string]any{ + "key": key, + } + + renderer.RenderNoLayout("lost_password_recover_ko", w, r, files, state) +} diff --git a/renderer/beneficiaries.go b/renderer/beneficiaries.go index 614cee8..a28ec8e 100644 --- a/renderer/beneficiaries.go +++ b/renderer/beneficiaries.go @@ -4,14 +4,16 @@ import ( "encoding/json" "html/template" "net/http" + + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" ) const beneficiariesMenu = "beneficiaries" type BeneficiariesListState struct { - Count int `json:"count"` - CacheId string `json:"cache_id"` - Beneficiaries []any `json:"beneficiaries"` + Count int `json:"count"` + CacheId string `json:"cache_id"` + Beneficiaries []mobilityaccountsstorage.Account `json:"beneficiaries"` } func (s BeneficiariesListState) JSON() template.JS { @@ -26,7 +28,7 @@ func (s BeneficiariesListState) JSONWithLimits(a int, b int) template.JS { return s.JSON() } -func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []any, cacheid string) { +func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string) { files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.list.files") state := NewState(r, renderer.ThemeConfig, beneficiariesMenu) diff --git a/renderer/vehicles.go b/renderer/vehicles.go index 0dd88d8..4ec0184 100644 --- a/renderer/vehicles.go +++ b/renderer/vehicles.go @@ -1,10 +1,14 @@ package renderer -import "net/http" +import ( + "net/http" + + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" +) const vehiclesMenu = "vehicles" -func (renderer *Renderer) VehiclesSearch(w http.ResponseWriter, r *http.Request, beneficiaries []any, searched bool, vehicles []any, beneficiary any, startdate any, enddate any) { +func (renderer *Renderer) VehiclesSearch(w http.ResponseWriter, r *http.Request, beneficiaries []mobilityaccountsstorage.Account, searched bool, vehicles []any, beneficiary any, startdate any, enddate any) { files := renderer.ThemeConfig.GetStringSlice("views.vehicles.search.files") state := NewState(r, renderer.ThemeConfig, vehiclesMenu) viewstate := map[string]any{ diff --git a/themes/default/config.yaml b/themes/default/config.yaml index 5fc9839..49aa1f9 100644 --- a/themes/default/config.yaml +++ b/themes/default/config.yaml @@ -114,9 +114,24 @@ views: groups: files: - web/layouts/auth/groups.html + lost_password: + init: + files: + - web/layouts/auth/lost-password-init.html + recover: + form: + files: + - web/layouts/auth/lost-password-recover.html + ko: + files: + - web/layouts/auth/lost-password-recover-ko.html onboarding: - files: - - web/layouts/auth/onboarding.html + form: + files: + - web/layouts/auth/onboarding.html + ko: + files: + - web/layouts/auth/onboarding-ko.html icons: svg: @@ -125,6 +140,7 @@ icons: hero:outline/calendar: hero:outline/chevron-right: hero:outline/cog: + hero:outline/document-arrow-down: hero:outline/document-text: hero:outline/home: hero:outline/map: @@ -164,4 +180,9 @@ emails: files: - emails/layout.html - emails/onboarding/existing-member.html - \ No newline at end of file + auth: + retrieve_password: + subject: PAROURSMOB - Réinitialisez votre mot de passe + files: + - emails/layout.html + - emails/auth/retrieve-password.html \ No newline at end of file diff --git a/themes/default/emails/auth/retrieve-password.html b/themes/default/emails/auth/retrieve-password.html new file mode 100644 index 0000000..0bc2260 --- /dev/null +++ b/themes/default/emails/auth/retrieve-password.html @@ -0,0 +1,5 @@ +{{define "content"}} +
Bonjour,
+Vous avez demandé à réinitialiser votre mot de passe pour {{.username}}
+Pour créer votre nouveau mot de passe, cliquez sur le lien suivant : http://localhost:9000/auth/lost-password/recover?key={{.key}}
+{{end}} \ No newline at end of file diff --git a/themes/default/emails/onboarding/new-administrator.html b/themes/default/emails/onboarding/new-administrator.html index f2e62d2..53687a3 100644 --- a/themes/default/emails/onboarding/new-administrator.html +++ b/themes/default/emails/onboarding/new-administrator.html @@ -1,5 +1,5 @@ {{define "content"}}Vous avez été ajouté comme administrateur de l'organisation {{.group}} sur PARCOURSMOB.
Vous devez créer votre compte pour y accéder.
-Pour créer votre compte PARCOURSMOB, cliquez sur : http://localhost:9000/onboarding?key={{.key}}
+Pour créer votre compte PARCOURSMOB, cliquez sur : http://localhost:9000/auth/onboarding?key={{.key}}
{{end}} \ No newline at end of file diff --git a/themes/default/emails/onboarding/new-member.html b/themes/default/emails/onboarding/new-member.html index 845d612..3c230aa 100644 --- a/themes/default/emails/onboarding/new-member.html +++ b/themes/default/emails/onboarding/new-member.html @@ -1,5 +1,5 @@ {{define "content"}}Vous avez été ajouté à l'organisation {{.group}} sur PARCOURSMOB.
Vous devez créer votre compte pour y accéder.
-Pour créer votre compte PARCOURSMOB, cliquez sur : http://localhost:9000/onboarding?key={{.key}}
+Pour créer votre compte PARCOURSMOB, cliquez sur : http://localhost:9000/auth/onboarding?key={{.key}}
{{end}} \ No newline at end of file diff --git a/themes/default/web/layouts/_partials/address_autocomplete.html b/themes/default/web/layouts/_partials/address_autocomplete.html index 174aa1c..3ffd1b2 100644 --- a/themes/default/web/layouts/_partials/address_autocomplete.html +++ b/themes/default/web/layouts/_partials/address_autocomplete.html @@ -3,7 +3,7 @@Ce lien de réinitialisation n'est plus actif. Vous l'avez déjà utilisé ou il a expiré. Vous pouvez redemander un nouveau mot de passe ou réessayer de vous connecter directement à PARCOURSMOB.
+Ce lien d'inscription n'est plus actif. Vous avez peut être déjà créé votre compte. Si ce n'est pas le cas, le lien a pu expirer : veuillez en demander un nouveau à l'administrateur PARCOURSMOB de votre structure.
+Aucun covoiturage disponible pour ce trajet.
+{{end}} + {{$first := true}} {{range .ViewState.carpools}} {{if $first}} diff --git a/themes/default/web/layouts/journeys/_partials/journeys-public-transit.html b/themes/default/web/layouts/journeys/_partials/journeys-public-transit.html index 5447076..59bbfab 100644 --- a/themes/default/web/layouts/journeys/_partials/journeys-public-transit.html +++ b/themes/default/web/layouts/journeys/_partials/journeys-public-transit.html @@ -1,5 +1,9 @@ {{define "journeys_public_transit"}} +{{ if eq (len .ViewState.journeys.Journeys) 0}} +Aucun transport en commun pour ce trajet.
+{{end}} + {{$first := true}} {{range .ViewState.journeys.Journeys}} {{if $first}} @@ -51,7 +55,7 @@{{if eq .Display.Network "Antibes"}}Envibus{{else}}{{.Display.Network}}{{end}} Ligne {{.Display.Label}}
+{{if eq .Display.Network "Antibes - Envibus"}}Envibus{{else}}{{.Display.Network}}{{end}} Ligne {{.Display.Label}}