diff --git a/README.md b/README.md index 6980828..ddb2dc0 100755 --- a/README.md +++ b/README.md @@ -10,5 +10,3 @@ This new version of PARCOURSMOB brings : - A configurable and themeable approach of rendering web pages : the default theme is located in the folder [themes/default/](themes/default/) - A modular architecture based on groups and access rights, using [COOPGO Groups Management](https://git.coopgo.io/coopgo-groups-management) - A distributed cache system through [etcd](https://etcd.io/) to handle distributed state management like pagination in a cloud native way - - diff --git a/config.go b/config.go index ac4eddb..19907f5 100755 --- a/config.go +++ b/config.go @@ -22,11 +22,105 @@ func ReadConfig() (*viper.Viper, error) { "session_key": "SESSION_KEY", }, }, + "storage": map[string]any{ + "files": map[string]any{ + "file_types": map[string]string{ + "driving_licence": "Permis de conduire", + "work_contract": "Contrat de travail", + "identity_proof": "Pièce d'identité", + "membership_form": "Bulletin d'adhésion", + "other": "Autre", + }, + }, + }, + "modules": map[string]any{ + "dashboard": map[string]any{ + "enabled": true, + }, + "beneficiaries": map[string]any{ + "enabled": true, + "validated_profile": map[string]any{ + "enabled": false, + "required": map[string]any{ + "fields": []string{}, + "documents": []string{}, + }, + }, + }, + "journeys": map[string]any{ + "enabled": true, + }, + "solidarity_transport": map[string]any{ + "enabled": true, + "drivers": map[string]any{ + "documents_types": []string{"membership_form", "driving_licence", "identity_proof", "other"}, + "validated_profile": map[string]any{ + "enabled": false, + "required": map[string]any{ + "fields": []string{}, + "documents": []string{}, + }, + }, + }, + }, + "organized_carpool": map[string]any{ + "enabled": true, + "drivers": map[string]any{ + "documents_types": []string{"membership_form", "driving_licence", "identity_proof", "other"}, + "validated_profile": map[string]any{ + "enabled": false, + "required": map[string]any{ + "fields": []string{}, + "documents": []string{}, + }, + }, + }, + }, + "vehicles": map[string]any{ + "enabled": true, + }, + "vehicles_management": map[string]any{ + "enabled": true, + }, + "agenda": map[string]any{ + "enabled": true, + }, + "directory": map[string]any{ + "enabled": true, + }, + "support": map[string]any{ + "enabled": true, + }, + }, "geo": map[string]any{ "pelias": map[string]any{ "url": "https://geocode.ridygo.fr", }, }, + "geography": map[string]any{ + "storage": map[string]any{ + "index": map[string]any{ + "type": "memory_rtree", + "bleve": map[string]any{ + "file": "index.bleve", + }, + }, + }, + "services": map[string]any{ + "grpc": map[string]any{ + "enable": true, + "port": 8080, + }, + }, + "data": map[string]any{ + "layers": map[string]string{ + "regions": "https://etalab-datasets.geo.data.gouv.fr/contours-administratifs/latest/geojson/regions-50m.geojson", + "departements": "https://etalab-datasets.geo.data.gouv.fr/contours-administratifs/latest/geojson/departements-50m.geojson", + "epci": "https://etalab-datasets.geo.data.gouv.fr/contours-administratifs/latest/geojson/epci-50m.geojson", + "communes": "https://etalab-datasets.geo.data.gouv.fr/contours-administratifs/latest/geojson/communes-50m.geojson", + }, + }, + }, } v := viper.New() for key, value := range defaults { diff --git a/go.mod b/go.mod index 5eea603..dc3d2ae 100755 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module git.coopgo.io/coopgo-apps/parcoursmob -go 1.23.3 +go 1.24.2 // replace git.coopgo.io/coopgo-platform/mobility-accounts => ../../coopgo-platform/mobility-accounts/ @@ -12,116 +12,167 @@ go 1.23.3 // replace git.coopgo.io/coopgo-platform/emailing => ../../coopgo-platform/emailing/ -replace git.coopgo.io/coopgo-platform/data-hub => ../../coopgo-platform/data-hub/ +// replace git.coopgo.io/coopgo-platform/data-hub => ../../coopgo-platform/data-hub/ + +// replace git.coopgo.io/coopgo-platform/solidarity-transport => ../../coopgo-platform/solidarity-transport/ + +// replace git.coopgo.io/coopgo-platform/carpool-service => ../../coopgo-platform/carpool-service/ + +// replace git.coopgo.io/coopgo-platform/multimodal-routing => ../../coopgo-platform/multimodal-routing/ + +// replace git.coopgo.io/coopgo-platform/payments => ../../coopgo-platform/payments/ + +// replace git.coopgo.io/coopgo-platform/geography => ../../coopgo-platform/geography/ + +// replace git.coopgo.io/coopgo-platform/sms => ../../coopgo-platform/sms/ require ( github.com/fogleman/gg v1.3.0 - github.com/go-playground/validator/v10 v10.11.0 + github.com/go-playground/validator/v10 v10.14.1 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/sessions v1.2.1 - github.com/paulmach/go.geojson v1.4.0 - github.com/spf13/viper v1.19.0 + github.com/paulmach/go.geojson v1.4.0 // indirect + github.com/spf13/viper v1.20.1 gitlab.scity.coop/maas/navitia-golang v0.0.0-20220429110621-5c22d6efdd0c go.etcd.io/etcd/client/v3 v3.5.12 golang.org/x/image v0.5.0 - golang.org/x/oauth2 v0.23.0 - google.golang.org/grpc v1.68.0 - google.golang.org/protobuf v1.35.2 + golang.org/x/oauth2 v0.25.0 + google.golang.org/grpc v1.71.1 + google.golang.org/protobuf v1.36.6 ) require ( git.coopgo.io/coopgo-platform/agenda v1.0.0 + git.coopgo.io/coopgo-platform/carpool-service v0.0.0-20250415082502-575b8129b727 git.coopgo.io/coopgo-platform/emailing v0.0.0-20250212064257-167ef5864260 git.coopgo.io/coopgo-platform/fleets v0.0.0-20230310144446-feb935f8bf4e git.coopgo.io/coopgo-platform/groups-management v0.0.0-20230310123255-5ef94ee0746c git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20230329105908-a76c0412a386 + git.coopgo.io/coopgo-platform/multimodal-routing v0.0.0-20250504230045-8680f560066a + git.coopgo.io/coopgo-platform/payments v0.0.0-20250616162131-77f7b00b8fc3 + git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536 + git.coopgo.io/coopgo-platform/sms v0.0.0-20250523074631-1f1e7fc6b7af + git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250616110116-d71da5accdfd github.com/arran4/golang-ical v0.3.1 github.com/coreos/go-oidc/v3 v3.11.0 github.com/gorilla/securecookie v1.1.1 github.com/minio/minio-go/v7 v7.0.43 - github.com/rs/zerolog v1.33.0 + github.com/paulmach/orb v0.11.1 + github.com/rs/zerolog v1.34.0 + github.com/stretchr/objx v0.5.2 github.com/xuri/excelize/v2 v2.7.1 ) require ( - github.com/sagikazarmark/locafero v0.6.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + git.coopgo.io/coopgo-platform/carpool-service/interoperability/ocss v0.0.0-20250429082239-a95cd6eb5523 // indirect + git.coopgo.io/coopgo-platform/geography v0.0.0-20250616160304-0285c9494673 // indirect + github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/bits-and-blooms/bitset v1.22.0 // indirect + github.com/blevesearch/bleve/v2 v2.5.2 // indirect + github.com/blevesearch/bleve_index_api v1.2.8 // indirect + github.com/blevesearch/geo v0.2.3 // indirect + github.com/blevesearch/go-faiss v1.0.25 // indirect + github.com/blevesearch/go-porterstemmer v1.0.3 // indirect + github.com/blevesearch/gtreap v0.1.1 // indirect + github.com/blevesearch/mmap-go v1.0.4 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect + github.com/blevesearch/segment v0.9.1 // indirect + github.com/blevesearch/snowballstem v0.9.0 // indirect + github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect + github.com/blevesearch/vellum v1.1.0 // indirect + github.com/blevesearch/zapx/v11 v11.4.2 // indirect + github.com/blevesearch/zapx/v12 v12.4.2 // indirect + github.com/blevesearch/zapx/v13 v13.4.2 // indirect + github.com/blevesearch/zapx/v14 v14.4.2 // indirect + github.com/blevesearch/zapx/v15 v15.4.2 // indirect + github.com/blevesearch/zapx/v16 v16.2.4 // indirect + github.com/bmatcuk/doublestar v1.3.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/gorilla/schema v1.4.1 // indirect + github.com/manterfield/go-mapreader v0.2.0 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + github.com/tidwall/geoindex v1.7.0 // indirect + github.com/tidwall/rtree v1.10.0 // indirect + github.com/twpayne/go-polyline v1.1.1 // indirect + github.com/zclconf/go-cty-yaml v1.1.0 // indirect + go.etcd.io/bbolt v1.4.0 // indirect + go.mongodb.org/mongo-driver/v2 v2.1.0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/tools v0.32.0 // indirect ) require ( - ariga.io/atlas v0.12.0 // indirect + ariga.io/atlas v0.32.0 // indirect git.coopgo.io/coopgo-platform/diags v0.0.0-20250212093351-64da61495c9d - github.com/agext/levenshtein v1.2.1 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/agext/levenshtein v1.2.3 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect - github.com/go-openapi/inflect v0.19.0 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-openapi/inflect v0.21.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/hcl/v2 v2.10.0 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/hashicorp/hcl/v2 v2.23.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.7 // indirect - github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mb0/wkt v0.0.0-20170420051526-a30afd545ee1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.0 // indirect - github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/montanaflynn/stats v0.7.1 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect - github.com/rs/xid v1.5.0 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect - github.com/sergi/go-diff v1.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/twpayne/go-geom v1.5.7 // indirect - github.com/wneessen/go-mail v0.5.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect - github.com/zclconf/go-cty v1.8.0 // indirect + github.com/zclconf/go-cty v1.16.2 // indirect go.etcd.io/etcd/api/v3 v3.5.12 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect - go.mongodb.org/mongo-driver v1.17.1 // indirect + go.mongodb.org/mongo-driver v1.17.3 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 3744835..4bca761 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,47 @@ -ariga.io/atlas v0.12.0 h1:jDfjxT3ppKhzqLS26lZv9ni7p9TVNrhy7SQquaF7bPs= -ariga.io/atlas v0.12.0/go.mod h1:+TR129FJZ5Lvzms6dvCeGWh1yR6hMvmXBhug4hrNIGk= +ariga.io/atlas v0.32.0 h1:y+77nueMrExLiKlz1CcPKh/nU7VSlWfBbwCShsJyvCw= +ariga.io/atlas v0.32.0/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w= git.coopgo.io/coopgo-platform/agenda v1.0.0 h1:rTHgva1JKKO0wAPlINegifMkHm+xOg3IWW4yQRy334w= git.coopgo.io/coopgo-platform/agenda v1.0.0/go.mod h1:/hToSla0p6SeWn1zo1MDrfxdmo7RBdZDkbLqCVituIM= +git.coopgo.io/coopgo-platform/carpool-service v0.0.0-20250415082502-575b8129b727 h1:HdE8EO1CUhwGOTeNeG6dftWtxvZIHWA7SvxBAA7Ab1s= +git.coopgo.io/coopgo-platform/carpool-service v0.0.0-20250415082502-575b8129b727/go.mod h1:edaXmIta2b5SfK1OHG/02uRAXkKZqj7dSMQoeBxfUew= +git.coopgo.io/coopgo-platform/carpool-service/interoperability/ocss v0.0.0-20250429082239-a95cd6eb5523 h1:hXoUOEZ+umiyR0SzYbGacJxiUbW4puw4phmkfTVfqPU= +git.coopgo.io/coopgo-platform/carpool-service/interoperability/ocss v0.0.0-20250429082239-a95cd6eb5523/go.mod h1:c9aJwNtY4PJuqAFYZ9afnx46UAZtWJ3P8ICZM02/DBA= git.coopgo.io/coopgo-platform/diags v0.0.0-20250212093351-64da61495c9d h1:fBxVvik4Cb/6d4+HAXZi9e8x8P9UBCJt8JcWgqnNjsE= git.coopgo.io/coopgo-platform/diags v0.0.0-20250212093351-64da61495c9d/go.mod h1:diyq11WNkgJ0kYHdT7SphXPMQUyoq4lRk7T6IgM5yPA= -git.coopgo.io/coopgo-platform/emailing v0.0.0-20241119141913-9836b30191c1 h1:gAhJ9wwlitMiETmnD4U2L6xLnVd5xE4hPX72IJ5s7+I= -git.coopgo.io/coopgo-platform/emailing v0.0.0-20241119141913-9836b30191c1/go.mod h1:EXy6NRvFfpW6yIHoZUixldkXrj1qzjCbTHpumDvzaKI= git.coopgo.io/coopgo-platform/emailing v0.0.0-20250212064257-167ef5864260 h1:Li3dotY6raKu9+oxEgICU7nwdomYpjgu19i3mZNiqTc= git.coopgo.io/coopgo-platform/emailing v0.0.0-20250212064257-167ef5864260/go.mod h1:6cvvjv0RLSwBthIQ4TiuZoXFGvQXZ55hNSJchWXAgB4= git.coopgo.io/coopgo-platform/fleets v0.0.0-20230310144446-feb935f8bf4e h1:eHahRTKlC8aBWYCd6LbXNcX8HoQhuZj31OFWrw0EL0U= git.coopgo.io/coopgo-platform/fleets v0.0.0-20230310144446-feb935f8bf4e/go.mod h1:s9OIFCNcjBAbBzRNHwoCTYV6kAntPG9CpT3GVweGdTY= +git.coopgo.io/coopgo-platform/geography v0.0.0-20250613055151-06d78317f9ad h1:hNjV2OrYBtSrQZE22YMKIT/iCGdVE30drEJD1pkDhhU= +git.coopgo.io/coopgo-platform/geography v0.0.0-20250613055151-06d78317f9ad/go.mod h1:TbR3g1Awa8hpAe6LR1z1EQbv2IBVgN5JQ/FjXfKX4K0= +git.coopgo.io/coopgo-platform/geography v0.0.0-20250616160304-0285c9494673 h1:cth7a8Mnx1C6C6F5rv7SoKVMHYpI/CioFubyi0xB+Dw= +git.coopgo.io/coopgo-platform/geography v0.0.0-20250616160304-0285c9494673/go.mod h1:TbR3g1Awa8hpAe6LR1z1EQbv2IBVgN5JQ/FjXfKX4K0= git.coopgo.io/coopgo-platform/groups-management v0.0.0-20230310123255-5ef94ee0746c h1:bY7PyrAgYY02f5IpDyf1WVfRqvWzivu31K6aEAYbWCw= git.coopgo.io/coopgo-platform/groups-management v0.0.0-20230310123255-5ef94ee0746c/go.mod h1:lozSy6qlIIYhvKKXscZzz28HAtS0qBDUTv5nofLRmYA= git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20230329105908-a76c0412a386 h1:v1JUdx8sknw2YYhFGz5cOAa1dEWNIBKvyiOpKr3RR+s= git.coopgo.io/coopgo-platform/mobility-accounts v0.0.0-20230329105908-a76c0412a386/go.mod h1:1typNYtO+PQT6KG77vs/PUv0fO60/nbeSGZL2tt1LLg= +git.coopgo.io/coopgo-platform/multimodal-routing v0.0.0-20250504230045-8680f560066a h1:Fl12RBdYDxYh7H2xluPhgTpaHKUNULWpsclBKNx2t8s= +git.coopgo.io/coopgo-platform/multimodal-routing v0.0.0-20250504230045-8680f560066a/go.mod h1:zDMfGVIvzuWV4Cw4bSi2kuEyMDbAfNfs7cGOAWNKfOo= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250523113636-a98e83924818 h1:7qIY7Ebh/sR6zCvK5nEsLCxljYwiwDf8KWIqSwqmGYU= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250523113636-a98e83924818/go.mod h1:32jMjsXTcdzifpsJOQ3Jv8bkieLqefJXWfwkZIZ/Ua4= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250610065650-b422c8adc3d7 h1:+FUcrwnQouOi/CfVyRNy7Lyv7Wm9XJ7y+qLaugMRl0g= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250610065650-b422c8adc3d7/go.mod h1:32jMjsXTcdzifpsJOQ3Jv8bkieLqefJXWfwkZIZ/Ua4= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250616110159-53d3a7f51f92 h1:mXQhmMkP5xh6oDcDQ6UJex0/LwOZgO3ci8IZTpSjQTg= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250616110159-53d3a7f51f92/go.mod h1:X2WqQN7ZLAucV9z1gPubkWChkHbmdOIxWdwn18DZ+YU= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250616162131-77f7b00b8fc3 h1:HGEo2E4IyprzshGKKeK7xMmorLNIGF8vbxa3zkBa+KM= +git.coopgo.io/coopgo-platform/payments v0.0.0-20250616162131-77f7b00b8fc3/go.mod h1:X2WqQN7ZLAucV9z1gPubkWChkHbmdOIxWdwn18DZ+YU= +git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536 h1:SllXX1VJXulfhNi+Pd0R9chksm8zO6gkWcTQ/uSMsdc= +git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536/go.mod h1:Nh7o15LlV0OuO9zxvJIs9FlelpeAaLYkXtFdgIkFrgg= +git.coopgo.io/coopgo-platform/sms v0.0.0-20250523074631-1f1e7fc6b7af h1:KxHim1dFcOVbFhRqelec8cJ65QBD2cma6eytW8llgYY= +git.coopgo.io/coopgo-platform/sms v0.0.0-20250523074631-1f1e7fc6b7af/go.mod h1:mad9D+WICDdpJzB+8H/wEVVbllK2mU6VLVByrppc9x0= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250528053224-d237401c81ba h1:CY4DxNQVpwF2OKTu+4+7KJZdM/TH4b3uOtKsI7PKTDw= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250528053224-d237401c81ba/go.mod h1:PKjHUTpP8VNm5bm5njsyj3m6bUpxOVMjoU+llDdfB9E= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250604135203-418c7152b665 h1:5CzA5T9IHjfhawogGO+ADxlUBN+va5xOEHTGAdI03ik= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250604135203-418c7152b665/go.mod h1:jnSYIECcH/nL8bLBwYD2WmJSPgWUC2rioOXxivWBTBM= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250610065958-2f0a45ced0a2 h1:OIkREZ/b9Vah+eV7cdarcaAkJ+ac39QAEENpJ8oi4Xw= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250610065958-2f0a45ced0a2/go.mod h1:jnSYIECcH/nL8bLBwYD2WmJSPgWUC2rioOXxivWBTBM= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250616110116-d71da5accdfd h1:2mvQWRfKHxihCxF1tOH1JgJinxqGDE96x4drlEloQNM= +git.coopgo.io/coopgo-platform/solidarity-transport v0.0.0-20250616110116-d71da5accdfd/go.mod h1:jnSYIECcH/nL8bLBwYD2WmJSPgWUC2rioOXxivWBTBM= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/DATA-DOG/go-sqlmock v1.3.2/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= @@ -23,20 +51,66 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= -github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= +github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= +github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/arran4/golang-ical v0.3.1 h1:v13B3eQZ9VDHTAvT6M11vVzxYgcYmjyPBE2eAZl3VZk= github.com/arran4/golang-ical v0.3.1/go.mod h1:LZWxF8ZIu/sjBVUCV0udiVPrQAgq3V0aa0RfbO99Qkk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= +github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8= +github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo= +github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y= +github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= +github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg= +github.com/blevesearch/geo v0.2.3/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= +github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U= +github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= +github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= +github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= +github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= +github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= +github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= +github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= +github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s= +github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8= +github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= +github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= +github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= +github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= +github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= +github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= +github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= +github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= +github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= +github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= +github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= +github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= +github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= +github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= +github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= +github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= +github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= +github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= +github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww= +github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= +github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= @@ -55,94 +129,103 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= -github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= -github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= -github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/inflect v0.21.2 h1:0gClGlGcxifcJR56zwvhaOulnNgnhc4qTAkob5ObnSM= +github.com/go-openapi/inflect v0.21.2/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= -github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= +github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= +github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manterfield/go-mapreader v0.2.0 h1:ZDJbja6bZCatRG725SDchdudqYnULyk04wSH58gvyqM= +github.com/manterfield/go-mapreader v0.2.0/go.mod h1:dYr8DHvSqfhrwTHBO1h8dLa9nUUnushqBYuWBns1Z0s= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -158,10 +241,8 @@ github.com/minio/minio-go/v7 v7.0.43/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASM github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -170,18 +251,26 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY= github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -193,66 +282,69 @@ github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7 github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= -github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/cities v0.1.0/go.mod h1:lV/HDp2gCcRcHJWqgt6Di54GiDrTZwh1aG2ZUPNbqa4= +github.com/tidwall/geoindex v1.7.0 h1:jtk41sfgwIt8MEDyC3xyKSj75iXXf6rjReJGDNPtR5o= +github.com/tidwall/geoindex v1.7.0/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I= +github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/rtree v1.10.0 h1:+EcI8fboEaW1L3/9oW/6AMoQ8HiEIHyR7bQOGnmz4Mg= +github.com/tidwall/rtree v1.10.0/go.mod h1:iDJQ9NBRtbfKkzZu02za+mIlaP+bjYPnunbSNidpbCQ= github.com/twpayne/go-geom v1.2.1/go.mod h1:90yvs0wf/gyT5eQ9W4v5WOZ9w/Xnrj5RMlA9XNKqxyA= github.com/twpayne/go-geom v1.5.7 h1:7fdceDUr03/MP7rAKOaTV6x9njMiQdxB/D0PDzMTCDc= github.com/twpayne/go-geom v1.5.7/go.mod h1:y4fTAQtLedXW8eG2Yo4tYrIGN1yIwwKkmA+K3iSHKBA= github.com/twpayne/go-kml v1.5.0/go.mod h1:g/OG8Q8JUxqFw8LGXE44W7osn1uXDAYaVFr1Yld43yc= github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8= -github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck= +github.com/twpayne/go-polyline v1.1.1 h1:/tSF1BR7rN4HWj4XKqvRUNrCiYVMCvywxTFVofvDV0w= +github.com/twpayne/go-polyline v1.1.1/go.mod h1:ybd9IWWivW/rlXPXuuckeKUyF3yrIim+iqA7kSl4NFY= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c= @@ -261,26 +353,46 @@ github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdV github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY= github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M= github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= -github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70= +github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= +github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= +github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= gitlab.scity.coop/maas/navitia-golang v0.0.0-20220429110621-5c22d6efdd0c h1:pCazzEsTvjDopl3bvo6H2f2xjo1cDjOZ9QpJRNFCc00= gitlab.scity.coop/maas/navitia-golang v0.0.0-20220429110621-5c22d6efdd0c/go.mod h1:M1U2osA6dYQF8zuJOTb/0O1F/Xgcb+4AkRdw+Un6Rp4= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg= go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= -go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= -go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= +go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.mongodb.org/mongo-driver/v2 v2.1.0 h1:/ELnVNjmfUKDsoBisXxuJL0noR9CfeUIrP7Yt3R+egg= +go.mongodb.org/mongo-driver/v2 v2.1.0/go.mod h1:AWiLRShSrk5RHQS3AEn3RL19rqOzVq49MCpWQ3x/huI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -292,20 +404,14 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -314,16 +420,12 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -331,33 +433,22 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -366,51 +457,30 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -419,35 +489,31 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/handlers/api/protected/protected.go b/handlers/api/protected/protected.go new file mode 100644 index 0000000..0a31990 --- /dev/null +++ b/handlers/api/protected/protected.go @@ -0,0 +1,32 @@ +package protected + +import ( + "net/http" + + "git.coopgo.io/coopgo-apps/parcoursmob/services" + "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + "github.com/spf13/viper" +) + +type ProtectedAPIHandler struct { + ApiKey string + idp *identification.IdentificationProvider + config *viper.Viper + services *services.ServicesHandler + cache cache.CacheHandler +} + +func NewProtectedAPIHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache cache.CacheHandler) (*ProtectedAPIHandler, error) { + return &ProtectedAPIHandler{ + ApiKey: cfg.GetString("services.api.api_key"), + idp: idp, + config: cfg, + services: svc, + cache: cache, + }, nil +} + +func (h *ProtectedAPIHandler) NotFound(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) +} diff --git a/handlers/api/protected/users.go b/handlers/api/protected/users.go new file mode 100644 index 0000000..bc57632 --- /dev/null +++ b/handlers/api/protected/users.go @@ -0,0 +1,65 @@ +package protected + +import ( + "context" + "encoding/json" + "net/http" + + groupsgrpc "git.coopgo.io/coopgo-platform/groups-management/grpcapi" + "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" + "git.coopgo.io/coopgo-platform/mobility-accounts/storage" + "github.com/rs/zerolog/log" +) + +func (h *ProtectedAPIHandler) Users(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + h.postUsers(w, r) + return + } + + w.WriteHeader(http.StatusMethodNotAllowed) +} + +func (h *ProtectedAPIHandler) postUsers(w http.ResponseWriter, r *http.Request) { + var user storage.Account + + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&user) + if err != nil { + log.Error().Err(err).Msg("could not read account input") + w.WriteHeader(http.StatusBadRequest) + return + } + + account, err := grpcapi.AccountFromStorageType(&user) + if err != nil { + log.Error().Err(err).Msg("could not generate protobuf for account") + w.WriteHeader(http.StatusBadRequest) + return + } + + resp, err := h.services.GRPC.MobilityAccounts.Register(context.Background(), &grpcapi.RegisterRequest{ + Account: account, + }) + if err != nil { + log.Error().Err(err).Msg("grpc request issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if g, ok := user.Metadata["import_in_group"]; ok { + if group, ok := g.(string); ok { + _, err = h.services.GRPC.GroupsManagement.Subscribe(context.Background(), &groupsgrpc.SubscribeRequest{ + Groupid: group, + Memberid: resp.Account.Id, + }) + if err != nil { + log.Error().Err(err).Msg("grpc request issue, groups") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + } + + w.WriteHeader(http.StatusOK) +} diff --git a/handlers/application/application.go b/handlers/application/application.go index 1b127c6..942dc54 100755 --- a/handlers/application/application.go +++ b/handlers/application/application.go @@ -25,7 +25,7 @@ type ApplicationHandler struct { func NewApplicationHandler(cfg *viper.Viper, svc *services.ServicesHandler, cache cache.CacheHandler, filestorage cache.FileStorage, emailing *emailing.Mailer) (*ApplicationHandler, error) { templates_root := cfg.GetString("templates.root") - renderer := renderer.NewRenderer(cfg, templates_root) + renderer := renderer.NewRenderer(cfg, templates_root, filestorage) return &ApplicationHandler{ config: cfg, Renderer: renderer, diff --git a/handlers/application/beneficiaries.go b/handlers/application/beneficiaries.go index 0d4e343..067e01a 100755 --- a/handlers/application/beneficiaries.go +++ b/handlers/application/beneficiaries.go @@ -28,21 +28,25 @@ import ( "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" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" ) 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"` + 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"` + OtherProperties any `json:"other_properties,omitempty"` } type Event_Beneficiary interface { @@ -192,7 +196,7 @@ func (h *ApplicationHandler) BeneficiaryDisplay(w http.ResponseWriter, r *http.R resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("cannot retrieve account") w.WriteHeader(http.StatusInternalServerError) return } @@ -305,6 +309,34 @@ func (h *ApplicationHandler) BeneficiaryDisplay(w http.ResponseWriter, r *http.R events_list = append(events_list, event) } } + solidarity, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(context.Background(), &gen.GetSolidarityTransportBookingsRequest{ + Passengerid: beneficiaryID, + StartDate: timestamppb.New(time.Now().Add(-36 * 730 * time.Hour)), + EndDate: timestamppb.New(time.Now().Add(12 * 730 * time.Hour)), + }) + solidarityTransportStats := map[string]int64{ + "count": 0, + "km": 0, + } + for _, s := range solidarity.Bookings { + b, _ := transformers.BookingProtoToType(s) + event := Event{ + NameVal: fmt.Sprintf("%s (%d km)", b.Journey.PassengerDrop.Properties.MustString("label", ""), b.Journey.PassengerDistance), + DateVal: b.Journey.PassengerPickupDate, + DateEndVal: b.Journey.PassengerPickupDate, + TypeVal: "Transport solidaire", + IDVal: b.Id, + DbVal: "/app/solidarity-transport/bookings/", + IconSet: "vehicle", + StatusVal: 1, + } + + events_list = append(events_list, event) + + solidarityTransportStats["count"] = solidarityTransportStats["count"] + 1 + solidarityTransportStats["km"] = solidarityTransportStats["km"] + b.Journey.PassengerDistance + + } sortByDate(events_list) diag := []diagsstorage.Diag{} @@ -349,7 +381,7 @@ func (h *ApplicationHandler) BeneficiaryDisplay(w http.ResponseWriter, r *http.R for i, d := range diag { diagsAny[i] = d } - h.Renderer.BeneficiaryDisplay(w, r, resp.Account.ToStorageType(), bookings, organizations, beneficiaries_file_types, file_types_map, documents, events_list, diagsAny) + h.Renderer.BeneficiaryDisplay(w, r, resp.Account.ToStorageType(), bookings, organizations, beneficiaries_file_types, file_types_map, documents, events_list, diagsAny, solidarityTransportStats) } func (h *ApplicationHandler) BeneficiaryUpdate(w http.ResponseWriter, r *http.Request) { @@ -409,6 +441,58 @@ func (h *ApplicationHandler) BeneficiaryUpdate(w http.ResponseWriter, r *http.Re h.Renderer.BeneficiaryUpdate(w, r, resp.Account.ToStorageType()) } +func (h *ApplicationHandler) BeneficiaryArchive(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + beneficiaryID := vars["beneficiaryid"] + + data, err := structpb.NewValue(map[string]any{ + "archived": true, + }) + + 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 { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) +} + +func (h *ApplicationHandler) BeneficiaryUnarchive(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + beneficiaryID := vars["beneficiaryid"] + + data, err := structpb.NewValue(map[string]any{ + "archived": false, + }) + + 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 { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) +} + func (h *ApplicationHandler) BeneficiaryPicture(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] @@ -496,7 +580,7 @@ func (h *ApplicationHandler) BeneficiaryDocumentDownload(w http.ResponseWriter, return } - http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) + // http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) } func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool { @@ -509,6 +593,18 @@ func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool { } } + archivedFilter, ok := r.URL.Query()["archived"] + if ok && archivedFilter[0] == "true" { + if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived { + return true + } + return false + } else { + if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived { + return false + } + } + return true } @@ -529,6 +625,7 @@ func (h *ApplicationHandler) beneficiaries(r *http.Request) ([]mobilityaccountss resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(context.TODO(), request) if err != nil { + log.Error().Err(err).Msg("issue in mobilityaccounts call") return accounts, err } @@ -566,6 +663,7 @@ func parseBeneficiariesForm(r *http.Request) (map[string]any, error) { FileNumber: r.PostFormValue("file_number"), Gender: r.PostFormValue("gender"), } + log.Trace().Any("address", r.PostFormValue("address")).Msg("address") if r.PostFormValue("address") != "" { var a any @@ -574,6 +672,15 @@ func parseBeneficiariesForm(r *http.Request) (map[string]any, error) { formData.Address = a } + log.Trace().Any("otther properties", r.PostFormValue("other_properties")).Msg("other propperties ?") + + if r.PostFormValue("other_properties") != "" { + var a any + json.Unmarshal([]byte(r.PostFormValue("other_properties")), &a) + + formData.OtherProperties = a + } + validate := formvalidators.New() if err := validate.Struct(formData); err != nil { return nil, err diff --git a/handlers/application/dashboard.go b/handlers/application/dashboard.go index 92eea91..fa52f3f 100755 --- a/handlers/application/dashboard.go +++ b/handlers/application/dashboard.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "sort" + "time" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" @@ -87,7 +88,9 @@ func (h *ApplicationHandler) Dashboard(w http.ResponseWriter, r *http.Request) { if err == nil { for _, b := range bookingsresp.Bookings { - bookings = append(bookings, b.ToStorageType()) + if b.Enddate.AsTime().After(time.Now()) { + bookings = append(bookings, b.ToStorageType()) + } } } diff --git a/handlers/application/journeys.go b/handlers/application/journeys.go index 117f8a9..64c510f 100755 --- a/handlers/application/journeys.go +++ b/handlers/application/journeys.go @@ -5,23 +5,33 @@ import ( "encoding/json" "fmt" "net/http" + "slices" "sort" "strconv" "strings" + "sync" "time" + "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" + carpoolproto "git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" + fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" groupstorage "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" + "git.coopgo.io/coopgo-platform/multimodal-routing/libs/transit/motis" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers" "github.com/google/uuid" "github.com/gorilla/mux" - geojson "github.com/paulmach/go.geojson" + "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" - "gitlab.scity.coop/maas/navitia-golang" - "gitlab.scity.coop/maas/navitia-golang/types" + + //"gitlab.scity.coop/maas/navitia-golang" + //"gitlab.scity.coop/maas/navitia-golang/types" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" ) var ( @@ -32,14 +42,16 @@ var ( func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Request) { r.ParseForm() var ( - journeys_results *navitia.JourneyResults - carpool_results any - vehicle_results []any + transit_results []*motis.Itinerary + carpool_results []*geojson.FeatureCollection + vehicle_results []fleetsstorage.Vehicle ) - availableDrivers := []mobilityaccountsstorage.Account{} - vehiclech := make(chan []any, 1) - navitiaCh := make(chan *navitia.JourneyResults, 1) - carpoolCh := make(chan any, 1) + // availableDrivers := []mobilityaccountsstorage.Account{} + drivers := map[string]mobilityaccountsstorage.Account{} + driverJourneys := []*gen.SolidarityTransportDriverJourney{} + var organizedCarpools []*carpoolproto.CarpoolServiceDriverJourney + // navitiaCh := make(chan *navitia.JourneyResults, 1) + // carpoolCh := make(chan []any, 1) locTime, errTime := time.LoadLocation("Europe/Paris") if errTime != nil { log.Panic().Err(errTime).Msg("Tried to load timezone location Europe/Paris. Error. Missing zones in container ?") @@ -51,15 +63,15 @@ func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Reque departure := r.FormValue("departure") destination := r.FormValue("destination") + kb_results := []any{} searched := false var ( departuregeo *geojson.Feature destinationgeo *geojson.Feature - journeys *navitia.JourneyResults - carpoolresults any - vehicles = []any{} + // journeys *navitia.JourneyResults + // carpoolresults []any ) if departuredate != "" && departuretime != "" && departure != "" && destination != "" { @@ -69,132 +81,191 @@ func (h *ApplicationHandler) JourneysSearch(w http.ResponseWriter, r *http.Reque departuregeo, err = geojson.UnmarshalFeature([]byte(departure)) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("error unmarshalling departure") w.WriteHeader(http.StatusBadRequest) return } destinationgeo, err = geojson.UnmarshalFeature([]byte(destination)) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("error unmarshalling destination") w.WriteHeader(http.StatusBadRequest) return } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - journeysRequest := func() { - // TODO make it a library - session, _ := navitia.NewCustom( - h.config.GetString("services.navitia.api_key"), - "https://api.navitia.io/v1", - &http.Client{}) + + // SOLIDARITY TRANSPORT + drivers, err = h.services.GetAccountsInNamespacesMap([]string{"solidarity_drivers", "organized_carpool_drivers"}) + if err != nil { + drivers = map[string]mobilityaccountsstorage.Account{} + } + + protodep, _ := transformers.GeoJsonToProto(departuregeo) + protodest, _ := transformers.GeoJsonToProto(destinationgeo) + + log.Debug().Time("departure time", departuredatetime).Msg("calling driver journeys with ...") + + res, err := h.services.GRPC.SolidarityTransport.GetDriverJourneys(context.Background(), &gen.GetDriverJourneysRequest{ + Departure: protodep, + Arrival: protodest, + DepartureDate: timestamppb.New(departuredatetime), + }) + if err != nil { + log.Error().Err(err).Msg("error in grpc call to GetDriverJourneys") + } else { + driverJourneys = slices.Collect(func(yield func(*gen.SolidarityTransportDriverJourney) bool) { + for _, dj := range res.DriverJourneys { + if a, ok := drivers[dj.DriverId].Data["archived"]; ok { + if archived, ok := a.(bool); ok { + if archived { + continue + } + } + } + if !yield(dj) { + return + } + } + }) + // res.DriverJourneys + sort.Slice(driverJourneys, func(i, j int) bool { + return driverJourneys[i].DriverDistance < driverJourneys[j].DriverDistance + }) + } + + // ORGANIZED CARPOOL + organizedCarpoolsRes, err := h.services.GRPC.CarpoolService.DriverJourneys(context.Background(), &carpoolproto.DriverJourneysRequest{ + DepartureLat: departuregeo.Point().Lat(), + DepartureLng: departuregeo.Point().Lon(), + ArrivalLat: destinationgeo.Point().Lat(), + ArrivalLng: destinationgeo.Point().Lon(), + DepartureDate: timestamppb.New(departuredatetime), + }) + if err != nil { + log.Error().Err(err).Msg("error retrieving organized carpools") + } else { + organizedCarpools = organizedCarpoolsRes.DriverJourneys + sort.Slice(organizedCarpools, func(i, j int) bool { + return *organizedCarpools[i].Distance < *organizedCarpools[j].Distance + }) + } + + var wg sync.WaitGroup + // CARPOOL OPERATORS + carpools := make(chan *geojson.FeatureCollection) + go h.services.InteropCarpool.Search(carpools, *departuregeo, *destinationgeo, departuredatetime) + wg.Add(1) + go func() { + defer wg.Done() + for c := range carpools { + carpool_results = append(carpool_results, c) + } + }() + + // TRANSIT + transitch := make(chan *motis.Itinerary) + go func(transitch chan *motis.Itinerary, departure *geojson.Feature, destination *geojson.Feature, datetime *time.Time) { + defer close(transitch) + // timetableView := false + searchWindows := 1800 + response, err := h.services.TransitRouting.PlanWithResponse(context.Background(), &motis.PlanParams{ + FromPlace: fmt.Sprintf("%f,%f", departure.Point().Lat(), departure.Point().Lon()), + ToPlace: fmt.Sprintf("%f,%f", destination.Point().Lat(), destination.Point().Lon()), + Time: datetime, + // TimetableView: &timetableView, + SearchWindow: &searchWindows, + }) if err != nil { - log.Error().Err(err).Msg("") - w.WriteHeader(http.StatusBadRequest) - navitiaCh <- nil + log.Error().Err(err).Msg("error retrieving transit data from MOTIS server") return } - - request := navitia.JourneyRequest{ - From: types.ID(fmt.Sprintf("%f", departuregeo.Geometry.Point[0]) + ";" + fmt.Sprintf("%f", departuregeo.Geometry.Point[1])), - To: types.ID(fmt.Sprintf("%f", destinationgeo.Geometry.Point[0]) + ";" + fmt.Sprintf("%f", destinationgeo.Geometry.Point[1])), - Date: departuredatetime.Add(-2 * time.Hour), - DateIsArrival: false, // TODO + for _, i := range response.JSON200.Itineraries { + transitch <- &i } - - journeys, err = session.Journeys(context.Background(), request) - if err != nil { - log.Error().Err(err).Msg("") - // w.WriteHeader(http.StatusBadRequest) - // return + }(transitch, departuregeo, destinationgeo, &departuredatetime) + wg.Add(1) + go func() { + defer wg.Done() + for itinerary := range transitch { + transit_results = append(transit_results, itinerary) } - navitiaCh <- journeys - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // SOLIDARITY TRANSPORT - drivers, err := h.services.GetAccountsInNamespace("solidarity_drivers") - if err != nil { - drivers = []mobilityaccountsstorage.Account{} - } - for _, a := range drivers { - if availabilities, ok := a.Data["solidarity_transport_availabilities"].([]any); ok { - for _, availability := range availabilities { - if av, ok := availability.(map[string]any); ok { - day := av["day"].(float64) - starttime := av["start_time"].(string) - endtime := av["end_time"].(string) - if departuretime >= starttime && departuretime <= endtime && departuredatetime.Weekday() == time.Weekday(int(day)) { - availableDrivers = append(availableDrivers, a) - break + }() + + // VEHICLES + vehiclech := make(chan fleetsstorage.Vehicle) + go h.vehicleRequest(vehiclech, departuredatetime.Add(-24*time.Hour), departuredatetime.Add(168*time.Hour)) + wg.Add(1) + go func() { + defer wg.Done() + for vehicle := range vehiclech { + vehicle_results = append(vehicle_results, vehicle) + } + slices.SortFunc(vehicle_results, sorting.VehiclesByDistanceFrom(*departuregeo)) + }() + // journeys_results = <-navitiaCh + wg.Wait() + // KNOWLEDGE BASE + departureGeo, _ := h.services.Geography.GeoSearch(departuregeo) + kbData := h.config.Get("knowledge_base") + if kb, ok := kbData.([]any); ok { + log.Debug().Msg("1") + for _, sol := range kb { + log.Debug().Msg("2") + if solution, ok := sol.(map[string]any); ok { + log.Debug().Msg("3") + if g, ok := solution["geography"]; ok { + log.Debug().Msg("4") + if geography, ok := g.([]any); ok { + log.Debug().Msg("5") + for _, gg := range geography { + log.Debug().Msg("6") + if geog, ok := gg.(map[string]any); ok { + log.Debug().Msg("7") + if layer, ok := geog["layer"].(string); ok { + log.Debug().Msg("8") + code := geog["code"] + log.Debug().Any("code", fmt.Sprintf("%s", code)).Any("departure geo", departureGeo[layer].Properties.MustString("code")).Msg("9") + if strings.Compare(fmt.Sprintf("%v", code), departureGeo[layer].Properties.MustString("code")) == 0 { + log.Debug().Msg("10") + kb_results = append(kb_results, solution) + break + } + + } + } + } } } } } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - //CARPOOL - carpoolRequest := func() { - carpoolrequest := "https://api.rdex.ridygo.fr/journeys.json" - - client := &http.Client{} - req, err := http.NewRequest("GET", carpoolrequest, nil) - if err != nil { - log.Error().Err(err).Msg("") - } - - req.URL.RawQuery = fmt.Sprintf( - "p[driver][state]=1&frequency=punctual&p[passenger][state]=0&p[from][latitude]=%f&p[from][longitude]=%f&p[to][latitude]=%f&p[to][longitude]=%f&p[outward][mindate]=%s&p[outward][maxdate]=%s", - departuregeo.Geometry.Point[1], departuregeo.Geometry.Point[0], - destinationgeo.Geometry.Point[1], destinationgeo.Geometry.Point[0], - departuredatetime.Format("2006-01-02"), departuredatetime.Format("2006-01-02")) - - req.Header.Set("X-API-KEY", "123456") - resp, err := client.Do(req) - if err != nil { - log.Error().Err(err).Msg("") - } - - if err == nil && resp.StatusCode == http.StatusOK { - err = json.NewDecoder(resp.Body).Decode(&carpoolresults) - if err != nil { - log.Error().Err(err).Msg("") - } - - if carpoolresults == nil { - carpoolresults = []any{} - } - } else { - carpoolresults = []any{} - } - carpoolCh <- carpoolresults - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Vehicles - vehicleRequest := func() { - vehiclerequest := &fleets.GetVehiclesRequest{ - Namespaces: []string{"parcoursmob"}, - } - vehicleresp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), vehiclerequest) - if err != nil { - log.Error().Err(err).Msg("") - w.WriteHeader(http.StatusInternalServerError) - } - for _, vehicle := range vehicleresp.Vehicles { - v := vehicle.ToStorageType() - if v.Free(departuredatetime.Add(-24*time.Hour), departuredatetime.Add(168*time.Hour)) { - vehicles = append(vehicles, v) - } - } - vehiclech <- vehicles - } - go journeysRequest() - go carpoolRequest() - go vehicleRequest() - carpool_results = <-carpoolCh - journeys_results = <-navitiaCh - vehicle_results = <-vehiclech } - h.Renderer.JourneysSearch(w, r, carpool_results, journeys_results, vehicle_results, searched, departuregeo, destinationgeo, departuredate, departuretime, availableDrivers) + beneficiaries, err := h.beneficiaries(r) + if err != nil { + log.Error().Err(err).Msg("issue retrieving beneficiaries") + w.WriteHeader(http.StatusInternalServerError) + return + } + + h.Renderer.JourneysSearch(w, r, carpool_results, transit_results, vehicle_results, searched, departuregeo, destinationgeo, departuredate, departuretime, driverJourneys, drivers, organizedCarpools, beneficiaries, kb_results) +} + +func (h *ApplicationHandler) vehicleRequest(vehiclech chan fleetsstorage.Vehicle, start time.Time, end time.Time) { + defer close(vehiclech) + vehiclerequest := &fleets.GetVehiclesRequest{ + Namespaces: []string{"parcoursmob"}, + } + vehicleresp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), vehiclerequest) + if err != nil { + log.Error().Err(err).Msg("") + return + } + for _, vehicle := range vehicleresp.Vehicles { + v := vehicle.ToStorageType() + if v.Free(start, end) { + vehiclech <- v + } + } } type GroupsModule []groupstorage.Group diff --git a/handlers/application/organized-carpool.go b/handlers/application/organized-carpool.go new file mode 100644 index 0000000..65cf9ed --- /dev/null +++ b/handlers/application/organized-carpool.go @@ -0,0 +1,639 @@ +package application + +import ( + "cmp" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "slices" + "strings" + "time" + + formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators" + filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + "git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto" + 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" + "github.com/paulmach/orb" + "github.com/paulmach/orb/geojson" + "github.com/rs/zerolog/log" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type OrganizedCarpoolDriversForm 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"` + AddressDestination any `json:"address_destination,omitempty"` + Gender string `json:"gender"` +} + +func (h *ApplicationHandler) OrganizedCarpoolOverview(w http.ResponseWriter, r *http.Request) { + accounts, err := h.organizedCarpoolDrivers(r) + if err != nil { + log.Error().Err(err).Msg("issue getting solidarity drivers") + accounts = []mobilityaccountsstorage.Account{} + } + + accountsMap := map[string]mobilityaccountsstorage.Account{} + + for _, a := range accounts { + accountsMap[a.ID] = a + } + + beneficiariesMap, err := h.services.GetBeneficiariesMap() + if err != nil { + beneficiariesMap = map[string]mobilityaccountsstorage.Account{} + } + + bookingsproto, err := h.services.GRPC.CarpoolService.GetCarpoolBookings(context.Background(), &proto.GetCarpoolBookingsRequest{ + MinDate: timestamppb.Now(), + MaxDate: timestamppb.New(time.Now().Add(24 * 365 * time.Hour)), + }) + if err != nil { + log.Error().Err(err).Msg("issue retreving bookings") + } + + bookings := []*proto.CarpoolServiceBooking{} + + if err == nil { + for _, b := range bookingsproto.Bookings { + // booking, _ := transformers.BookingProtoToType(b) + bookings = append(bookings, b) + } + } + + slices.SortFunc(accounts, func(a, b mobilityaccountsstorage.Account) int { + return strings.Compare( + strings.ToLower(fmt.Sprintf("%s %s", a.Data["first_name"].(string), a.Data["last_name"].(string))), + strings.ToLower(fmt.Sprintf("%s %s", b.Data["first_name"].(string), b.Data["last_name"].(string))), + ) + }) + slices.SortFunc(bookings, func(a, b *proto.CarpoolServiceBooking) int { + return cmp.Compare(a.PassengerPickupDate.AsTime().Unix(), b.PassengerPickupDate.AsTime().Unix()) + }) + + h.Renderer.OrganizedCarpoolOverview(w, r, accounts, accountsMap, beneficiariesMap, bookings) +} + +func (h *ApplicationHandler) organizedCarpoolDrivers(r *http.Request) ([]mobilityaccountsstorage.Account, error) { + accounts := []mobilityaccountsstorage.Account{} + + request := &mobilityaccounts.GetAccountsRequest{ + Namespaces: []string{"organized_carpool_drivers"}, + } + + resp, err := h.services.GRPC.MobilityAccounts.GetAccounts(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 (h *ApplicationHandler) OrganizedCarpoolCreateDriver(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + dataMap, err := parseOrganizedCarpoolDriversForm(r) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusBadRequest) + return + } + + data, err := structpb.NewValue(dataMap) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + request := &mobilityaccounts.RegisterRequest{ + Account: &mobilityaccounts.Account{ + Namespace: "organized_carpool_drivers", + Data: data.GetStructValue(), + }, + } + + resp, err := h.services.GRPC.MobilityAccounts.Register(context.TODO(), request) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", resp.Account.Id), http.StatusFound) + return + } + + h.Renderer.OrganizedCarpoolCreateDriver(w, r) +} + +func (h *ApplicationHandler) OrganizedCarpoolDriverDisplay(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + documents := h.filestorage.List(filestorage.PREFIX_ORGANIZED_CARPOOL_DRIVERS + "/" + driverID) + + driver, err := h.services.GetAccount(driverID) + if err != nil { + log.Error().Err(err).Msg("Issue retrieving driver account") + w.WriteHeader(http.StatusInternalServerError) + return + } + + log.Info().Any("driver data", driver.Data).Msg("driver retrieved") + + trips := []*geojson.FeatureCollection{} + + resp, err := h.services.GRPC.CarpoolService.GetRegularRoutes(context.Background(), &proto.GetRegularRoutesRequest{ + UserId: driverID, + }) + + for _, r := range resp.Routes { + t, err := geojson.UnmarshalFeatureCollection([]byte(r.Serialized)) + if err != nil { + log.Error().Err(err).Msg("could not unmarshall feature collection") + continue + } + + trips = append(trips, t) + } + + h.Renderer.OrganizedCarpoolDriverDisplay(w, r, driver, trips, documents) +} + +func (h *ApplicationHandler) OrganizedCarpoolArchiveDriver(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + data, _ := structpb.NewValue(map[string]any{ + "archived": true, + }) + + request := &mobilityaccounts.UpdateDataRequest{ + Account: &mobilityaccounts.Account{ + Id: driverID, + Namespace: "organized_carpool_drivers", + Data: data.GetStructValue(), + }, + } + + resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", resp.Account.Id), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolUnarchiveDriver(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + data, _ := structpb.NewValue(map[string]any{ + "archived": false, + }) + + request := &mobilityaccounts.UpdateDataRequest{ + Account: &mobilityaccounts.Account{ + Id: driverID, + Namespace: "organized_carpool_drivers", + Data: data.GetStructValue(), + }, + } + + resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", resp.Account.Id), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolDriverDocuments(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + // r.ParseForm() + r.ParseMultipartForm(100 * 1024 * 1024) + + document_type := r.FormValue("type") + document_name := r.FormValue("name") + + file, header, err := r.FormFile("file-upload") + if err != nil { + log.Error().Err(err).Msg("") + return + } + defer file.Close() + + fileid := uuid.NewString() + + metadata := map[string]string{ + "type": document_type, + "name": document_name, + } + + log.Debug().Any("metadata", metadata).Msg("Metadata") + + if err := h.filestorage.Put(file, filestorage.PREFIX_ORGANIZED_CARPOOL_DRIVERS, fmt.Sprintf("%s/%s_%s", driverID, fileid, header.Filename), header.Size, metadata); err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolDocumentDownload(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + document := vars["document"] + + file, info, err := h.filestorage.Get(filestorage.PREFIX_ORGANIZED_CARPOOL_DRIVERS, fmt.Sprintf("%s/%s", driverID, document)) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", info.ContentType) + if _, err = io.Copy(w, file); err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + // http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolAddTrip(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + log.Error().Msg("Wrong method") + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + if err := r.ParseForm(); err != nil { + log.Error().Err(err).Msg("error parsong availabilities form") + w.WriteHeader(http.StatusInternalServerError) + return + } + + vars := mux.Vars(r) + driverID := vars["driverid"] + + trips := []*proto.CarpoolFeatureCollection{} + + outwardtime := r.PostFormValue("outwardtime") + returntime := r.PostFormValue("returntime") + dep := r.PostFormValue("address_departure") + dest := r.PostFormValue("address_destination") + + departure, err := geojson.UnmarshalFeature([]byte(dep)) + if err != nil { + log.Error().Err(err).Msg("failed parsong departure geojson") + w.WriteHeader(http.StatusInternalServerError) + return + } + destination, err := geojson.UnmarshalFeature([]byte(dest)) + if err != nil { + log.Error().Err(err).Msg("failed parsong departure geojson") + w.WriteHeader(http.StatusInternalServerError) + return + } + + outwardroute, err := h.services.Routing.Route([]orb.Point{departure.Point(), destination.Point()}) + if err != nil { + log.Error().Err(err).Msg("failed calling route search") + w.WriteHeader(http.StatusInternalServerError) + return + } + returnroute, err := h.services.Routing.Route([]orb.Point{destination.Point(), departure.Point()}) + if err != nil { + log.Error().Err(err).Msg("failed calling route search") + w.WriteHeader(http.StatusInternalServerError) + return + } + + outwardschedules := []map[string]any{} + returnschedules := []map[string]any{} + + if r.PostFormValue("days.monday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "MON", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "MON", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.tuesday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "TUE", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "TUE", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.wednesday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "WED", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "WED", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.thursday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "THU", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "THU", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.friday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "FRI", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "FRI", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.saturday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "SAT", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "SAT", + "time_of_day": returntime, + }) + } + if r.PostFormValue("days.sunday") == "on" { + outwardschedules = append(outwardschedules, map[string]any{ + "day": "SUN", + "time_of_day": outwardtime, + }) + returnschedules = append(returnschedules, map[string]any{ + "day": "SUN", + "time_of_day": returntime, + }) + } + + outward_fc := geojson.NewFeatureCollection() + outward_fc.Append(departure) + outward_fc.Append(destination) + outward_fc.ExtraMembers = geojson.Properties{} + outward_fc.ExtraMembers["properties"] = map[string]any{ + "is_driver": true, + "is_passenger": false, + "user": mobilityaccountsstorage.Account{ + ID: driverID, + }, + "polyline": outwardroute.Summary.Polyline, + "schedules": outwardschedules, + "driver_options": map[string]any{}, + "passenger_options": map[string]any{}, + } + outwardtrip, err := outward_fc.MarshalJSON() + if err != nil { + log.Error().Err(err).Msg("failed parsong return geojson") + w.WriteHeader(http.StatusInternalServerError) + return + } + + return_fc := geojson.NewFeatureCollection() + return_fc.Append(destination) + return_fc.Append(departure) + return_fc.ExtraMembers = geojson.Properties{} + return_fc.ExtraMembers["properties"] = map[string]any{ + "is_driver": true, + "is_passenger": false, + "user": mobilityaccountsstorage.Account{ + ID: driverID, + }, + "polyline": returnroute.Summary.Polyline, + "schedules": returnschedules, + "driver_options": map[string]any{}, + "passenger_options": map[string]any{}, + } + returntrip, err := return_fc.MarshalJSON() + if err != nil { + log.Error().Err(err).Msg("failed parsong return geojson") + w.WriteHeader(http.StatusInternalServerError) + return + } + + trips = append(trips, &proto.CarpoolFeatureCollection{ + Serialized: string(outwardtrip), + }) + trips = append(trips, &proto.CarpoolFeatureCollection{ + Serialized: string(returntrip), + }) + + req := &proto.CreateRegularRoutesRequest{ + Routes: trips, + } + _, err = h.services.GRPC.CarpoolService.CreateRegularRoutes(context.Background(), req) + if err != nil { + log.Error().Err(err).Msg("could not create regular routes") + w.WriteHeader(http.StatusInternalServerError) + return + } + + log.Info().Msg("Finished creating carpool routes") + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolDeleteTrip(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + tripID := vars["tripid"] + + req := &proto.DeleteRegularRoutesRequest{ + Ids: []string{tripID}, + } + + _, err := h.services.GRPC.CarpoolService.DeleteRegularRoutes(context.Background(), req) + if err != nil { + log.Error().Err(err).Msg("could not delete regular routes") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) OrganizedCarpoolJourney(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverId := vars["driverid"] + journeyId := vars["journeyid"] + + var passenger mobilityaccountsstorage.Account + passengerId := r.URL.Query().Get("passengerid") + log.Info().Str("journeyid", journeyId).Str("driverid", driverId).Str("passengerid", passengerId).Msg("driver journey") + + journeyResp, err := h.services.GRPC.CarpoolService.GetPlannedTrip(context.Background(), &proto.GetPlannedTripRequest{ + Id: journeyId, + }) + if err != nil { + log.Error().Err(err).Msg("could not get driver journey") + w.WriteHeader(http.StatusNotFound) + return + } + journey, err := geojson.UnmarshalFeatureCollection([]byte(journeyResp.PlannedTrip.Serialized)) + if err != nil { + log.Error().Err(err).Msg("could not unmarshal driver journey") + w.WriteHeader(http.StatusNotFound) + return + } + + if r.Method == "POST" { + if passengerId == "" { + log.Error().Err(err).Msg("could not get driver journey") + w.WriteHeader(http.StatusNotFound) + return + } + + /*if _, err := h.services.GRPC.CarpoolService.CreateBooking(context.Background(), &proto.CreateCarpoolBookingRequest{ + Booking: &proto.CarpoolServiceBooking{ + Passenger: &proto.CarpoolServiceUser{ + Id: passengerId, + }, + Driver: &proto.CarpoolServiceUser{ + Id: driverId, + }, + } + PassengerId: passengerId, + DriverId: driverId, + DriverJourneyId: journeyId, + }); err != nil { + log.Error().Err(err).Msg("cannot create booking") + w.WriteHeader(http.StatusInternalServerError) + return + }*/ + http.Redirect(w, r, fmt.Sprintf("/app/organized_carpool/"), http.StatusFound) + return + } + + driver, err := h.services.GetAccount(driverId) + if err != nil { + log.Error().Err(err).Msg("could not get driver") + w.WriteHeader(http.StatusNotFound) + return + } + if passengerId != "" { + passenger, err = h.services.GetAccount(passengerId) + if err != nil { + log.Error().Err(err).Msg("could not get account") + w.WriteHeader(http.StatusNotFound) + return + } + } + + beneficiaries, err := h.beneficiaries(r) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusBadRequest) + return + } + + /*driverjourney, err := transformers.DriverJourneyProtoToType(journey.DriverJourney) + if err != nil { + log.Error().Err(err).Msg("could not transform driver journey type") + w.WriteHeader(http.StatusBadRequest) + return + }*/ + + h.Renderer.OrganizedCarpoolJourney(w, r, journey, driver, passenger, beneficiaries) +} + +func parseOrganizedCarpoolDriversForm(r *http.Request) (map[string]any, error) { + if err := r.ParseForm(); err != nil { + return nil, err + } + + log.Info().Any("form content", r.Form).Msg("parsing form") + + 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 := OrganizedCarpoolDriversForm{ + 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 + } + + if r.PostFormValue("address_destination") != "" { + var a any + json.Unmarshal([]byte(r.PostFormValue("address_destination")), &a) + + formData.AddressDestination = 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 +} diff --git a/handlers/application/sms.go b/handlers/application/sms.go new file mode 100644 index 0000000..4e234b9 --- /dev/null +++ b/handlers/application/sms.go @@ -0,0 +1,58 @@ +package application + +import ( + "errors" + "net/http" + + "github.com/rs/zerolog/log" +) + +func (h *ApplicationHandler) SendSMS(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + referer := r.Referer() + + if err := r.ParseForm(); err != nil { + log.Error().Err(err).Msg("Bad request") + w.WriteHeader(http.StatusBadRequest) + return + } + + message := r.PostFormValue("message") + beneficiaryid := r.PostFormValue("beneficiaryid") + + h.GenerateSMS(beneficiaryid, message) + + http.Redirect(w, r, referer, http.StatusFound) +} + +func (h *ApplicationHandler) GenerateSMS(recipientid string, message string) error { + recipient, err := h.services.GetAccount(recipientid) + if err != nil { + log.Error().Err(err).Msg("user not found") + return err + } + + pn, ok := recipient.Data["phone_number"] + if !ok { + log.Error().Msg("Beneficiary doesn't have a phone number") + return errors.New("missing phone number") + } + phoneNumber, ok := pn.(string) + if !ok { + log.Error().Msg("phone number type error") + return errors.New("phone number type error") + } + + sender := h.config.GetString("service_name") + + err = h.services.SMS.Send(phoneNumber, message, sender) + if err != nil { + log.Error().Err(err).Msg("cannot send SMS") + return err + } + + return nil +} diff --git a/handlers/application/solidarity-transport-ext.go b/handlers/application/solidarity-transport-ext.go new file mode 100644 index 0000000..14a5b47 --- /dev/null +++ b/handlers/application/solidarity-transport-ext.go @@ -0,0 +1,80 @@ +package application + +import ( + "context" + "net/http" + + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers" + "github.com/gorilla/mux" + "github.com/rs/zerolog/log" +) + +func (h *ApplicationHandler) SolidarityTransportExternalBookingProposal(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bookingId := vars["bookingid"] + + resp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(context.Background(), &gen.GetSolidarityTransportBookingRequest{ + Id: bookingId, + }) + + booking, err := transformers.BookingProtoToType(resp.Booking) + if err != nil { + log.Error().Err(err).Msg("could not transform booking type") + w.WriteHeader(http.StatusBadRequest) + return + } + + driver, err := h.services.GetAccount(booking.DriverId) + if err != nil { + log.Error().Err(err).Msg("driver retrieval issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + + passenger, err := h.services.GetAccount(booking.PassengerId) + if err != nil { + log.Error().Err(err).Msg("passenger retrieval issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if r.Method == "POST" { + if err = r.ParseForm(); err != nil { + log.Error().Err(err).Msg("error parsing form data") + } + message := r.FormValue("message") + action := r.FormValue("action") + var status string + if action == "confirm" { + status = "VALIDATED" + } else if action == "cancel" { + status = "CANCELLED" + } else if action == "waitconfirmation" { + status = "WAITING_CONFIRMATION" + } + if status != "" { + if _, err := h.services.GRPC.SolidarityTransport.UpdateSolidarityTransportBookingStatus(context.Background(), &gen.UpdateSolidarityTransportBookingStatusRequest{ + BookingId: bookingId, + NewStatus: status, + Reason: "Refusé par le bénévole", + }); err != nil { + log.Error().Err(err).Msg("update booking status issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + booking.Status = status + if status == "VALIDATED" { + h.GenerateSMS(passenger.ID, message) + } else if status == "CANCELLED" { + if err := h.creditWallet(passenger.ID, booking.Journey.Price.Amount); err != nil { + log.Error().Err(err).Msg("could not credit wallet") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + } + } + + h.Renderer.SolidarityTransportExternalBookingDisplay(w, r, booking, driver, passenger) +} diff --git a/handlers/application/solidarity-transport.go b/handlers/application/solidarity-transport.go index dcdb23e..c771737 100644 --- a/handlers/application/solidarity-transport.go +++ b/handlers/application/solidarity-transport.go @@ -1,23 +1,34 @@ package application import ( + "cmp" "context" "encoding/json" - "errors" "fmt" + "io" "net/http" - "reflect" + "slices" "sort" + "strconv" + "strings" "time" + mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" + "git.coopgo.io/coopgo-platform/payments/pricing" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers" + "git.coopgo.io/coopgo-platform/solidarity-transport/types" + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/paulmach/orb/geojson" + "github.com/rs/zerolog/log" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" - 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" - "github.com/rs/zerolog/log" - "google.golang.org/protobuf/types/known/structpb" + filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" ) type DriversForm struct { @@ -43,16 +54,68 @@ const ( func (h *ApplicationHandler) SolidarityTransportOverview(w http.ResponseWriter, r *http.Request) { accounts, err := h.solidarityDrivers(r) if err != nil { - log.Error().Err(err).Msg("") - w.WriteHeader(http.StatusBadRequest) - return + log.Error().Err(err).Msg("issue getting solidarity drivers") + accounts = []mobilityaccountsstorage.Account{} } - sort.Sort(sorting.SolidarityDriversByName(accounts)) + accountsMap := map[string]mobilityaccountsstorage.Account{} - // cacheid := uuid.NewString() - // h.cache.PutWithTTL(cacheid, accounts, 1*time.Hour) - h.Renderer.SolidarityTransportOverview(w, r, accounts) + for _, a := range accounts { + accountsMap[a.ID] = a + } + + beneficiariesMap, err := h.services.GetBeneficiariesMap() + if err != nil { + beneficiariesMap = map[string]mobilityaccountsstorage.Account{} + } + + bookingsproto, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(context.Background(), &gen.GetSolidarityTransportBookingsRequest{ + StartDate: timestamppb.Now(), + EndDate: timestamppb.New(time.Now().Add(24 * 365 * time.Hour)), + }) + if err != nil { + log.Error().Err(err).Msg("issue retreving bookings") + } + + bookings := []*types.Booking{} + + if err == nil { + for _, b := range bookingsproto.Bookings { + booking, _ := transformers.BookingProtoToType(b) + bookings = append(bookings, booking) + } + } + bookingshistoryproto, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(context.Background(), &gen.GetSolidarityTransportBookingsRequest{ + EndDate: timestamppb.Now(), + StartDate: timestamppb.New(time.Now().Add(-1 * 24 * 365 * time.Hour)), + }) + if err != nil { + log.Error().Err(err).Msg("issue retreving bookings") + } + + bookingshistory := []*types.Booking{} + + if err == nil { + for _, b := range bookingshistoryproto.Bookings { + booking, _ := transformers.BookingProtoToType(b) + bookingshistory = append(bookingshistory, booking) + } + } + + slices.SortFunc(accounts, func(a, b mobilityaccountsstorage.Account) int { + return strings.Compare( + strings.ToLower(fmt.Sprintf("%s %s", a.Data["first_name"].(string), a.Data["last_name"].(string))), + strings.ToLower(fmt.Sprintf("%s %s", b.Data["first_name"].(string), b.Data["last_name"].(string))), + ) + }) + slices.SortFunc(bookings, func(a, b *types.Booking) int { + return cmp.Compare(a.Journey.PassengerPickupDate.Unix(), b.Journey.PassengerPickupDate.Unix()) + }) + slices.SortFunc(bookingshistory, func(a, b *types.Booking) int { + return cmp.Compare(a.Journey.PassengerPickupDate.Unix(), b.Journey.PassengerPickupDate.Unix()) + }) + + h.Renderer.SolidarityTransportOverview(w, r, accounts, accountsMap, beneficiariesMap, bookings, bookingshistory) } func (h *ApplicationHandler) SolidarityTransportCreateDriver(w http.ResponseWriter, r *http.Request) { @@ -92,7 +155,7 @@ func (h *ApplicationHandler) SolidarityTransportCreateDriver(w http.ResponseWrit h.Renderer.SolidarityTransportCreateDriver(w, r) } -func (h *ApplicationHandler) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request) { +func (h *ApplicationHandler) SolidarityTransportUpdateDriver(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] @@ -102,7 +165,94 @@ func (h *ApplicationHandler) SolidarityTransportDriverDisplay(w http.ResponseWri w.WriteHeader(http.StatusInternalServerError) return } - h.Renderer.SolidarityTransportDriverDisplay(w, r, driver) + + if r.Method == "POST" { + dataMap, err := parseBeneficiariesForm(r) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusBadRequest) + return + } + + data, err := structpb.NewValue(dataMap) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + request := &mobilityaccounts.UpdateDataRequest{ + Account: &mobilityaccounts.Account{ + Id: driverID, + Namespace: "solidarity_drivers", + Data: data.GetStructValue(), + }, + } + + resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", resp.Account.Id), http.StatusFound) + return + } + + h.Renderer.SolidarityTransportUpdateDriver(w, r, driver) +} + +func (h *ApplicationHandler) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + documents := h.filestorage.List(filestorage.PREFIX_SOLIDARITY_TRANSPORT_DRIVERS + "/" + driverID) + + driver, err := h.services.GetAccount(driverID) + if err != nil { + log.Error().Err(err).Msg("Issue retrieving driver account") + w.WriteHeader(http.StatusInternalServerError) + return + } + + availabilities := []*gen.DriverRegularAvailability{} + + resp, err := h.services.GRPC.SolidarityTransport.GetDriverRegularAvailabilities(context.Background(), &gen.GetDriverRegularAvailabilitiesRequest{DriverId: driverID}) + if err != nil { + log.Error().Err(err).Msg("error calling GetDriverRegularAvailabilities") + } else { + availabilities = resp.Results + } + sort.Sort(sorting.SolidarityAvailabilitiesByDay(availabilities)) + + bookingsresp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(context.Background(), &gen.GetSolidarityTransportBookingsRequest{Driverid: driverID}) + if err != nil { + log.Error().Err(err).Msg("could not get driver bookings") + } + log.Debug().Any("bookings", bookingsresp.Bookings).Msg("bookings list") + kmnb := int64(0) + bookings := slices.Collect(func(yield func(*gen.SolidarityTransportBooking) bool) { + for _, b := range bookingsresp.Bookings { + log.Debug().Str("bokking status", b.Status).Msg("candidate booking") + if b.Status == "VALIDATED" { + kmnb = kmnb + b.Journey.DriverDistance + if !yield(b) { + return + } + } + } + }) + log.Debug().Any("bookings", bookings).Msg("bookings list") + + stats := map[string]any{ + "bookings": map[string]any{ + "count": len(bookings), + "km": kmnb, + }, + } + + h.Renderer.SolidarityTransportDriverDisplay(w, r, driver, availabilities, documents, bookings, stats) } func (h *ApplicationHandler) SolidarityTransportAddAvailability(w http.ResponseWriter, r *http.Request) { @@ -121,105 +271,164 @@ func (h *ApplicationHandler) SolidarityTransportAddAvailability(w http.ResponseW vars := mux.Vars(r) driverID := vars["driverid"] - availabilities := []any{} + availabilities := []*gen.DriverRegularAvailability{} + + starttime := r.PostFormValue("starttime") + endtime := r.PostFormValue("endtime") + address := r.PostFormValue("address") if r.PostFormValue("days.monday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Monday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Monday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } if r.PostFormValue("days.tuesday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Tuesday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Tuesday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } - if r.PostFormValue("days.Wednesday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Wednesday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + if r.PostFormValue("days.wednesday") == "on" { + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Wednesday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } if r.PostFormValue("days.thursday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Thursday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Thursday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } if r.PostFormValue("days.friday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Friday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Friday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } if r.PostFormValue("days.saturday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Saturday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Saturday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } if r.PostFormValue("days.sunday") == "on" { - a := map[string]any{ - "id": uuid.NewString(), - "day": Sunday, - "start_time": r.PostFormValue("starttime"), - "end_time": r.PostFormValue("endtime"), + a := &gen.DriverRegularAvailability{ + DriverId: driverID, + Day: Sunday, + StartTime: starttime, + EndTime: endtime, + Address: &gen.GeoJsonFeature{ + Serialized: address, + }, } availabilities = append(availabilities, a) } - account, err := h.services.GetAccount(driverID) + req := &gen.AddDriverRegularAvailabilitiesRequest{ + Availabilities: availabilities, + } + _, err := h.services.GRPC.SolidarityTransport.AddDriverRegularAvailabilities(context.Background(), req) if err != nil { - log.Error().Err(err).Msg("driver not found") - w.WriteHeader(http.StatusNotFound) + log.Error().Err(err).Msg("could not add availabilities") + w.WriteHeader(http.StatusInternalServerError) return } - existing_availabilities, ok := account.Data["solidarity_transport_availabilities"].([]any) - if !ok { - existing_availabilities = []any{} + log.Info().Msg("Finished creating availabilities") + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) SolidarityTransportUnarchiveDriver(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + data, _ := structpb.NewValue(map[string]any{ + "archived": false, + }) + + request := &mobilityaccounts.UpdateDataRequest{ + Account: &mobilityaccounts.Account{ + Id: driverID, + Namespace: "solidarity_transport_drivers", + Data: data.GetStructValue(), + }, } - for _, av := range availabilities { - existing_availabilities = append(existing_availabilities, av) - } - - account.Data["solidarity_transport_availabilities"] = existing_availabilities - data, err := structpb.NewValue(account.Data) + resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } - request := &mobilityaccounts.UpdateDataRequest{ - Account: &mobilityaccounts.Account{ - Id: account.ID, - Namespace: account.Namespace, - Data: data.GetStructValue(), - }, - } - _, err = h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", resp.Account.Id), http.StatusFound) +} + +func (h *ApplicationHandler) SolidarityTransportDriverDocuments(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + // r.ParseForm() + r.ParseMultipartForm(100 * 1024 * 1024) + + document_type := r.FormValue("type") + document_name := r.FormValue("name") + + file, header, err := r.FormFile("file-upload") if err != nil { + log.Error().Err(err).Msg("") + return + } + defer file.Close() + + fileid := uuid.NewString() + + metadata := map[string]string{ + "type": document_type, + "name": document_name, + } + + log.Debug().Any("metadata", metadata).Msg("Metadata") + + if err := h.filestorage.Put(file, filestorage.PREFIX_SOLIDARITY_TRANSPORT_DRIVERS, fmt.Sprintf("%s/%s_%s", driverID, fileid, header.Filename), header.Size, metadata); err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return @@ -228,61 +437,402 @@ func (h *ApplicationHandler) SolidarityTransportAddAvailability(w http.ResponseW http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) } +func (h *ApplicationHandler) SolidarityTransportDocumentDownload(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + document := vars["document"] + + file, info, err := h.filestorage.Get(filestorage.PREFIX_SOLIDARITY_TRANSPORT_DRIVERS, fmt.Sprintf("%s/%s", driverID, document)) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", info.ContentType) + if _, err = io.Copy(w, file); err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + // http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) SolidarityTransportArchiveDriver(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverID := vars["driverid"] + + data, _ := structpb.NewValue(map[string]any{ + "archived": true, + }) + + request := &mobilityaccounts.UpdateDataRequest{ + Account: &mobilityaccounts.Account{ + Id: driverID, + Namespace: "solidarity_transport_drivers", + Data: data.GetStructValue(), + }, + } + + resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", resp.Account.Id), http.StatusFound) +} + func (h *ApplicationHandler) SolidarityTransportDeleteAvailability(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] availabilityID := vars["availabilityid"] - account, err := h.services.GetAccount(driverID) + req := &gen.DeleteDriverRegularAvailabilityRequest{ + DriverId: driverID, + AvailabilityId: availabilityID, + } + + if _, err := h.services.GRPC.SolidarityTransport.DeleteDriverRegularAvailability(context.Background(), req); err != nil { + log.Error().Err(err).Msg("could not delete availability") + w.WriteHeader(http.StatusNotFound) + return + } + + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) +} + +func (h *ApplicationHandler) SolidarityTransportDriverJourney(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + driverId := vars["driverid"] + journeyId := vars["journeyid"] + + var passenger mobilityaccountsstorage.Account + passengerId := r.URL.Query().Get("passengerid") + log.Info().Str("journeyid", journeyId).Str("driverid", driverId).Msg("driver journey") + + journey, err := h.services.GRPC.SolidarityTransport.GetDriverJourney(context.Background(), &gen.GetDriverJourneyRequest{ + DriverId: driverId, + JourneyId: journeyId, + }) if err != nil { - log.Error().Err(err).Msg("driver not found") + log.Error().Err(err).Msg("could not get driver journey") w.WriteHeader(http.StatusNotFound) return } + if passengerId != "" { + passenger, err = h.services.GetAccount(passengerId) + if err != nil { + log.Error().Err(err).Msg("could not get account") + w.WriteHeader(http.StatusNotFound) + return + } + } - existing_availabilities, ok := account.Data["solidarity_transport_availabilities"].([]any) - if !ok { - log.Error().Err(errors.New("no availability found")).Msg("no availability") + j, err := transformers.DriverJourneyProtoToType(journey.DriverJourney) + if err != nil { + log.Error().Err(err).Msg("could not get driver journey") w.WriteHeader(http.StatusNotFound) return } + solidarity, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(context.Background(), &gen.GetSolidarityTransportBookingsRequest{ + Passengerid: passengerId, + StartDate: timestamppb.New(time.Now().Add(-12 * 730 * time.Hour)), + EndDate: timestamppb.New(time.Now().Add(12 * 730 * time.Hour)), + }) - new_availabilities := []any{} - - for _, av := range existing_availabilities { - log.Info().Str("type", reflect.TypeOf(av).Name()).Msg("info on type") - if m, ok := av.(map[string]any); ok { - if id, ok2 := m["id"].(string); ok2 { - if id != availabilityID { - new_availabilities = append(new_availabilities, av) + priority := false + if a, ok := passenger.Data["other_properties"]; ok { + if b, ok := a.(map[string]any); ok { + if c, ok := b["status"]; ok { + if p, ok := c.(string); ok { + priority = (p == "Prioritaire") } } } } - account.Data["solidarity_transport_availabilities"] = new_availabilities - data, err := structpb.NewValue(account.Data) - if err != nil { - log.Error().Err(err).Msg("") - w.WriteHeader(http.StatusInternalServerError) - return + history := 0 + if op, ok := passenger.Data["other_properties"]; ok { + if op_map, ok := op.(map[string]any); ok { + if pst, ok := op_map["previous_solidarity_transport"]; ok { + if pst_str, ok := pst.(string); ok { + if pst_str != "" { + if n, err := strconv.Atoi(pst_str); err == nil { + history = history + n + } else { + log.Error().Err(err).Str("n", pst_str).Msg("string to int conversion error") + } + } + } + } + } } - request := &mobilityaccounts.UpdateDataRequest{ - Account: &mobilityaccounts.Account{ - Id: account.ID, - Namespace: account.Namespace, - Data: data.GetStructValue(), + history = history + len(solidarity.Bookings) + + log.Debug().Any("history", history).Msg("history check") + + benefParams := pricing.BeneficiaryParams{} + if passengerId == "" { + benefParams = pricing.BeneficiaryParams{ + Address: h.pricingGeography(j.PassengerPickup), + History: 99, + Priority: false, + } + } else { + log.Debug().Any("passenger data", passenger.Data).Msg("0") + var passengerGeo pricing.GeographyParams + if pa, ok := passenger.Data["address"]; ok { + jsonpa, err := json.Marshal(pa) + if err == nil { + passGeojson, err := geojson.UnmarshalFeature(jsonpa) + if err == nil { + passengerGeo = h.pricingGeography(passGeojson) + } + } + } + benefParams = pricing.BeneficiaryParams{ + Address: passengerGeo, + History: history, + Priority: priority, + } + } + pricing, err := h.services.Pricing.Prices(pricing.PricingParams{ + MobilityType: "solidarity_transport", + Beneficiary: benefParams, + SharedMobility: pricing.SharedMobilityParams{ + DriverDistance: journey.DriverJourney.DriverDistance, + PassengerDistance: journey.DriverJourney.PassengerDistance, + Departure: h.pricingGeography(j.PassengerPickup), + Destination: h.pricingGeography(j.PassengerDrop), + OutwardOnly: journey.DriverJourney.Noreturn, }, + }) + if err != nil { + log.Error().Err(err).Msg("error in pricing calculation") } - _, err = h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) + + journey.DriverJourney.Price.Amount = pricing["passenger"].Amount + journey.DriverJourney.Price.Currency = "EUR" + + if r.Method == "POST" { + if passengerId == "" { + log.Error().Err(err).Msg("could not get driver journey") + w.WriteHeader(http.StatusNotFound) + return + } + + if err := r.ParseForm(); err != nil { + log.Error().Err(err).Msg("could not parse form input") + w.WriteHeader(http.StatusBadRequest) + return + } + + rwt := r.PostFormValue("return_waiting_time") + return_waiting_time := int64(0) + return_waiting_time, err := strconv.ParseInt(rwt, 10, 64) + if err == nil { + log.Error().Err(err).Msg("return waiting time error") + } + motivation := r.PostFormValue("motivation") + + data, err := structpb.NewStruct(map[string]any{ + "motivation": motivation, + }) + if err != nil { + log.Error().Err(err).Msg("issue creating data struct") + } + + bookingRes, err := h.services.GRPC.SolidarityTransport.BookDriverJourney(context.Background(), &gen.BookDriverJourneyRequest{ + PassengerId: passengerId, + DriverId: driverId, + DriverJourneyId: journeyId, + ReturnWaitingDuration: return_waiting_time * int64(time.Minute), + PriceAmount: journey.DriverJourney.Price.Amount, + PriceCurrency: journey.DriverJourney.Price.Currency, + Data: data, + }) + if err != nil { + log.Error().Err(err).Msg("cannot create booking") + w.WriteHeader(http.StatusInternalServerError) + return + } + + message := r.PostFormValue("message") + doNotSend := r.PostFormValue("do_not_send") + send_message := strings.ReplaceAll(message, "{booking_id}", bookingRes.Booking.Id) + log.Debug().Str("message", send_message).Str("do not send", doNotSend).Msg("Driver journey booked : sending message") + if doNotSend != "on" { + h.GenerateSMS(driverId, send_message) + + if err := h.creditWallet(passengerId, float64(-1)*journey.DriverJourney.Price.Amount); err != nil { + log.Error().Err(err).Msg("could not credit wallet") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/"), http.StatusFound) + return + } + + driver, err := h.services.GetAccount(driverId) + if err != nil { + log.Error().Err(err).Msg("could not get driver") + w.WriteHeader(http.StatusNotFound) + return + } + + beneficiaries, err := h.beneficiaries(r) if err != nil { log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusBadRequest) + return + } + + driverjourney, err := transformers.DriverJourneyProtoToType(journey.DriverJourney) + if err != nil { + log.Error().Err(err).Msg("could not transform driver journey type") + w.WriteHeader(http.StatusBadRequest) + return + } + + h.Renderer.SolidarityTransportDriverJourney(w, r, driverjourney, driver, passenger, beneficiaries) +} + +func (h *ApplicationHandler) SolidarityTransportDriverJourneyToggleNoreturn(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + journeyId := vars["journeyid"] + + _, err := h.services.GRPC.SolidarityTransport.ToggleSolidarityTransportNoreturn(context.Background(), &gen.ToggleSolidarityTransportNoreturnRequest{ + JourneyId: journeyId, + }) + if err != nil { + log.Error().Err(err).Msg("could not get driver journey") + w.WriteHeader(http.StatusNotFound) + return + } + log.Debug().Msg("here") + http.Redirect(w, r, r.Referer(), http.StatusFound) +} + +func (h *ApplicationHandler) SolidarityTransportBookingDisplay(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bookingId := vars["bookingid"] + + resp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(context.Background(), &gen.GetSolidarityTransportBookingRequest{ + Id: bookingId, + }) + if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + log.Error().Err(err).Msg("could not parse form") + w.WriteHeader(http.StatusBadRequest) + return + } + + driverDistanceStr := r.PostFormValue("driver_distance") + + driverDistance, err := strconv.Atoi(driverDistanceStr) + if err != nil { + log.Error().Err(err).Msg("could not read distance") + w.WriteHeader(http.StatusBadRequest) + return + } + resp.Booking.Journey.DriverDistance = int64(driverDistance) + + _, err = h.services.GRPC.SolidarityTransport.UpdateSolidarityTransportBooking(context.Background(), &gen.UpdateSolidarityTransportBookingRequest{ + Booking: resp.Booking, + }) + if err != nil { + log.Error().Err(err).Msg("could not update booking") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + + booking, err := transformers.BookingProtoToType(resp.Booking) + if err != nil { + log.Error().Err(err).Msg("could not transform booking type") + w.WriteHeader(http.StatusBadRequest) + return + } + + driver, err := h.services.GetAccount(booking.DriverId) + if err != nil { + log.Error().Err(err).Msg("driver retrieval issue") w.WriteHeader(http.StatusInternalServerError) return } - http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound) + passenger, err := h.services.GetAccount(booking.PassengerId) + if err != nil { + log.Error().Err(err).Msg("passenger retrieval issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + + h.Renderer.SolidarityTransportBookingDisplay(w, r, booking, driver, passenger) +} + +func (h *ApplicationHandler) SolidarityTransportBookingStatus(action string) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bookingId := vars["bookingid"] + reason := "" + + status := "" + if action == "confirm" { + status = "VALIDATED" + } else if action == "cancel" { + status = "CANCELLED" + if r.Method == "POST" { + r.ParseForm() + reason = r.PostFormValue("reason") + } + } else if action == "waitconfirmation" { + status = "WAITING_CONFIRMATION" + } + + if status != "" { + if _, err := h.services.GRPC.SolidarityTransport.UpdateSolidarityTransportBookingStatus(context.Background(), &gen.UpdateSolidarityTransportBookingStatusRequest{ + BookingId: bookingId, + NewStatus: status, + Reason: reason, + }); err != nil { + log.Error().Err(err).Msg("update booking status issue") + w.WriteHeader(http.StatusInternalServerError) + return + } + if status == "CANCELLED" { + booking, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(context.Background(), &gen.GetSolidarityTransportBookingRequest{ + Id: bookingId, + }) + if err == nil { + if err := h.creditWallet(booking.Booking.PassengerId, booking.Booking.Journey.Price.Amount); err != nil { + log.Error().Err(err).Msg("could not credit wallet") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + } + if status == "WAITING_CONFIRMATION" { + booking, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(context.Background(), &gen.GetSolidarityTransportBookingRequest{ + Id: bookingId, + }) + if err == nil { + if err := h.creditWallet(booking.Booking.PassengerId, float64(-1)*booking.Booking.Journey.Price.Amount); err != nil { + log.Error().Err(err).Msg("could not credit wallet") + w.WriteHeader(http.StatusInternalServerError) + return + } + } + } + } + http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/bookings/%s", bookingId), http.StatusFound) + } } func parseDriversForm(r *http.Request) (map[string]any, error) { @@ -356,3 +906,22 @@ func (h *ApplicationHandler) solidarityDrivers(r *http.Request) ([]mobilityaccou return accounts, err } + +func (h *ApplicationHandler) pricingGeography(loc *geojson.Feature) pricing.GeographyParams { + if loc == nil { + return pricing.GeographyParams{} + } + + geo, err := h.services.Geography.GeoSearch(loc) + if err != nil { + log.Error().Err(err).Msg("issue in geosearch") + return pricing.GeographyParams{} + } + return pricing.GeographyParams{ + Location: loc, + CityCode: geo["communes"].Properties.MustString("code"), + IntercommunalityCode: geo["epci"].Properties.MustString("code"), + RegionCode: geo["regions"].Properties.MustString("code"), + DepartmentCode: geo["departements"].Properties.MustString("code"), + } +} diff --git a/handlers/application/support.go b/handlers/application/support.go index 5fec249..c5dab6d 100755 --- a/handlers/application/support.go +++ b/handlers/application/support.go @@ -4,7 +4,6 @@ import ( "net/http" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" - "git.coopgo.io/coopgo-platform/emailing" "github.com/rs/zerolog/log" ) @@ -13,7 +12,6 @@ type Message struct { } func (h *ApplicationHandler) SupportSend(w http.ResponseWriter, r *http.Request) { - c := r.Context().Value(identification.ClaimsKey) if c == nil { log.Error().Msg("no current user claims") @@ -23,11 +21,10 @@ func (h *ApplicationHandler) SupportSend(w http.ResponseWriter, r *http.Request) current_user_claims := c.(map[string]any) comment := r.PostFormValue(("comment")) - if r.Method == "POST" { user_email := current_user_claims["email"].(string) - + data := map[string]any{ "key": comment, "user": user_email, @@ -35,10 +32,10 @@ func (h *ApplicationHandler) SupportSend(w http.ResponseWriter, r *http.Request) log.Debug().Str("user_email", user_email).Msg("Sending message") - if err := h.emailing.Send("support.request", "support@parcoursmob.fr", data, emailing.WithReplyTo(user_email), emailing.WithTLSOpportunistic()); err != nil { + if err := h.emailing.Send("support.request", "support@parcoursmob.fr", data); err != nil { log.Error().Err(err).Msg("error sending email") w.WriteHeader(http.StatusInternalServerError) - return + return } http.Redirect(w, r, "/app/", http.StatusFound) return diff --git a/handlers/application/vehicles-management.go b/handlers/application/vehicles-management.go index 9e64727..edc3ceb 100755 --- a/handlers/application/vehicles-management.go +++ b/handlers/application/vehicles-management.go @@ -11,8 +11,6 @@ import ( "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" - diags "git.coopgo.io/coopgo-platform/diags/grpcapi" - diagsstorage "git.coopgo.io/coopgo-platform/diags/storage" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" @@ -205,30 +203,30 @@ func (h *ApplicationHandler) VehiclesFleetDisplay(w http.ResponseWriter, r *http w.WriteHeader(http.StatusInternalServerError) return } - diag := []diagsstorage.Diag{} + /*diag := []diagsstorage.Diag{} diagsrequest := &diags.GetDiagsRequest{ Namespaces: []string{"parcoursmob_vehicles"}, } - diagsresp, err := h.services.GRPC.Diags.GetDiags(context.TODO(), diagsrequest) - if err != nil { - log.Error().Err(err).Msg("did not retrieve diags") - w.WriteHeader(http.StatusInternalServerError) - return - } - - for _, d := range diagsresp.Diags { - diagData := d.Data.AsMap() - if vehicle, ok := diagData["vehicle"].(string); ok && vehicle == vehicleid { - diag = append(diag, d.ToStorageType()) + diagsresp, err := h.services.GRPC.Diags.GetDiags(context.TODO(), diagsrequest) + if err != nil { + log.Error().Err(err).Msg("did not retrieve diags") + w.WriteHeader(http.StatusInternalServerError) + return } - } - diagsAny := make([]any, len(diag)) - for i, d := range diag { + for _, d := range diagsresp.Diags { + diagData := d.Data.AsMap() + if vehicle, ok := diagData["vehicle"].(string); ok && vehicle == vehicleid { + diag = append(diag, d.ToStorageType()) + } + } */ + + diagsAny := []any{} + /*for i, d := range diag { diagsAny[i] = d - } + }*/ h.Renderer.VehiclesFleetDisplay(w, r, resp.Vehicle.ToStorageType(), beneficiaries, diagsAny) } @@ -362,30 +360,30 @@ func (h ApplicationHandler) VehicleManagementBookingDisplay(w http.ResponseWrite documents := h.filestorage.List(filestorage.PREFIX_BOOKINGS + "/" + bookingid) file_types_map := h.config.GetStringMapString("storage.files.file_types") - diag := []diagsstorage.Diag{} + /* diag := []diagsstorage.Diag{} - diagsrequest := &diags.GetDiagsRequest{ - Namespaces: []string{"parcoursmob_bookings"}, - } - - diagsresp, err := h.services.GRPC.Diags.GetDiags(context.TODO(), diagsrequest) - if err != nil { - log.Error().Err(err).Msg("") - w.WriteHeader(http.StatusInternalServerError) - return - } - - for _, d := range diagsresp.Diags { - diagData := d.Data.AsMap() - if booking, ok := diagData["booking"].(string); ok && booking == bookingid { - diag = append(diag, d.ToStorageType()) + diagsrequest := &diags.GetDiagsRequest{ + Namespaces: []string{"parcoursmob_bookings"}, } - } - diagsAny := make([]any, len(diag)) - for i, d := range diag { + diagsresp, err := h.services.GRPC.Diags.GetDiags(context.TODO(), diagsrequest) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + for _, d := range diagsresp.Diags { + diagData := d.Data.AsMap() + if booking, ok := diagData["booking"].(string); ok && booking == bookingid { + diag = append(diag, d.ToStorageType()) + } + }*/ + + diagsAny := []any{} + /*for i, d := range diag { diagsAny[i] = d - } + }*/ h.Renderer.VehicleManagementBookingDisplay(w, r, booking, booking.Vehicle, beneficiary, groupresp.Group.ToStorageType(), documents, file_types_map, alternatives, diagsAny) } diff --git a/handlers/application/vehicles.go b/handlers/application/vehicles.go index e7ca6a9..29d44aa 100755 --- a/handlers/application/vehicles.go +++ b/handlers/application/vehicles.go @@ -2,9 +2,11 @@ package application import ( "context" + "encoding/json" "fmt" "io" "net/http" + "slices" "sort" "strings" "time" @@ -12,8 +14,6 @@ import ( "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" - diags "git.coopgo.io/coopgo-platform/diags/grpcapi" - diagsstorage "git.coopgo.io/coopgo-platform/diags/storage" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" "git.coopgo.io/coopgo-platform/fleets/storage" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" @@ -22,6 +22,7 @@ import ( mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" "github.com/google/uuid" "github.com/gorilla/mux" + "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -33,7 +34,7 @@ func (h ApplicationHandler) VehiclesSearch(w http.ResponseWriter, r *http.Reques beneficiarydocuments := []filestorage.FileInfo{} - vehicles := []any{} + vehicles := []storage.Vehicle{} searched := false start := r.FormValue("startdate") end := r.FormValue("enddate") @@ -100,6 +101,21 @@ func (h ApplicationHandler) VehiclesSearch(w http.ResponseWriter, r *http.Reques vehicles = append(vehicles, v) } + // Sort vehicles if beneficiary address is set + if beneficiaryAddress, ok := beneficiary.Data["address"]; ok { + beneficiaryAddressJson, err := json.Marshal(beneficiaryAddress) + if err == nil { + beneficiaryAddressGeojson, err := geojson.UnmarshalFeature(beneficiaryAddressJson) + if err == nil { + slices.SortFunc(vehicles, sorting.VehiclesByDistanceFrom(*beneficiaryAddressGeojson)) + } else { + log.Error().Err(err).Msg("error transforming beneficiary address to GeoJSON") + } + } else { + log.Error().Err(err).Msg("error transforming beneficiary address to JSON") + } + } + beneficiarydocuments = h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiary.ID) } @@ -307,7 +323,7 @@ func (h ApplicationHandler) VehicleBookingDisplay(w http.ResponseWriter, r *http documents := h.filestorage.List(filestorage.PREFIX_BOOKINGS + "/" + bookingid) file_types_map := h.config.GetStringMapString("storage.files.file_types") - diag := []diagsstorage.Diag{} + /*diag := []diagsstorage.Diag{} diagsrequest := &diags.GetDiagsRequest{ Namespaces: []string{"parcoursmob_bookings"}, @@ -325,12 +341,12 @@ func (h ApplicationHandler) VehicleBookingDisplay(w http.ResponseWriter, r *http if booking, ok := diagData["booking"].(string); ok && booking == bookingid { diag = append(diag, d.ToStorageType()) } - } + } */ - diagsAny := make([]any, len(diag)) - for i, d := range diag { + diagsAny := []any{} + /*for i, d := range diag { diagsAny[i] = d - } + }*/ h.Renderer.VehicleBookingDisplay(w, r, booking, booking.Vehicle, beneficiaryresp.Account.ToStorageType(), groupresp.Group.ToStorageType(), documents, file_types_map, diagsAny) } diff --git a/handlers/application/wallets.go b/handlers/application/wallets.go new file mode 100644 index 0000000..5a4ca66 --- /dev/null +++ b/handlers/application/wallets.go @@ -0,0 +1,69 @@ +package application + +import ( + "context" + "net/http" + "strconv" + + "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" + "github.com/gorilla/mux" + "github.com/rs/zerolog/log" +) + +func (h ApplicationHandler) CreditWallet(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + userid := vars["userid"] + + if r.Method != "POST" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + r.ParseForm() + + amountStr := r.FormValue("amount") + + amount, err := strconv.ParseFloat(amountStr, 64) + if err != nil { + log.Error().Err(err).Msg("could not read amount") + w.WriteHeader(http.StatusBadRequest) + return + } + + if err := h.creditWallet(userid, amount); err != nil { + log.Error().Err(err).Msg("could not credit wallet") + w.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(w, r, r.Referer(), http.StatusFound) +} + +func (h *ApplicationHandler) creditWallet(userid string, amount float64) error { + account, err := h.services.GetAccount(userid) + if err != nil { + log.Error().Err(err).Msg("could not retrieve account") + return err + } + + if account.Data["wallet"] == nil { + account.Data["wallet"] = float64(0) + } + + account.Data["wallet"] = account.Data["wallet"].(float64) + amount + accountproto, err := grpcapi.AccountFromStorageType(&account) + if err != nil { + log.Error().Err(err).Msg("account type transformation issue") + return err + } + + _, err = h.services.GRPC.MobilityAccounts.UpdateData(context.Background(), &grpcapi.UpdateDataRequest{ + Account: accountproto, + }) + if err != nil { + log.Error().Err(err).Msg("account type transformation issue") + return err + } + + return nil +} diff --git a/handlers/auth/auth.go b/handlers/auth/auth.go index 43d42bd..92eea63 100755 --- a/handlers/auth/auth.go +++ b/handlers/auth/auth.go @@ -18,9 +18,9 @@ type AuthHandler struct { emailing *emailing.Mailer } -func NewAuthHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache cache.CacheHandler, emailing *emailing.Mailer) (*AuthHandler, error) { +func NewAuthHandler(cfg *viper.Viper, idp *identification.IdentificationProvider, svc *services.ServicesHandler, cache cache.CacheHandler, emailing *emailing.Mailer, filestorage cache.FileStorage) (*AuthHandler, error) { templates_root := cfg.GetString("templates.root") - renderer := renderer.NewRenderer(cfg, templates_root) + renderer := renderer.NewRenderer(cfg, templates_root, filestorage) return &AuthHandler{ idp: idp, config: cfg, diff --git a/handlers/exports/fleets.go b/handlers/exports/fleets.go index 2b1d64a..35db848 100755 --- a/handlers/exports/fleets.go +++ b/handlers/exports/fleets.go @@ -17,7 +17,6 @@ import ( ) func (h *ExportsHandler) Bookings(w http.ResponseWriter, r *http.Request) { - vehicles := map[string]fleetsstorage.Vehicle{} bookings := []fleetsstorage.Booking{} reequest := &fleets.GetVehiclesRequest{ @@ -112,7 +111,6 @@ func (h *ExportsHandler) Bookings(w http.ResponseWriter, r *http.Request) { if v3, ok := v2["display_name"].(string); ok { bookedby = v3 } - } } @@ -170,5 +168,141 @@ func (h *ExportsHandler) Bookings(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Transfer-Encoding", "binary") w.Header().Set("Expires", "0") f.Write(w) - +} + +func (h *ExportsHandler) AllBookings(w http.ResponseWriter, r *http.Request) { + vehicles := map[string]fleetsstorage.Vehicle{} + bookings := []fleetsstorage.Booking{} + reequest := &fleets.GetVehiclesRequest{ + Namespaces: []string{"parcoursmob"}, + } + reesp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), reequest) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + beneficiaries_ids := []string{} + + for _, vehicle := range reesp.Vehicles { + + v := vehicle.ToStorageType() + + for _, b := range v.Bookings { + bookings = append(bookings, b) + beneficiaries_ids = append(beneficiaries_ids, b.Driver) + } + + vehicles[vehicle.Id] = v + + } + + groups := map[string]groupsstorage.Group{} + + admingroups, err := h.services.GRPC.GroupsManagement.GetGroups(context.TODO(), &groupsmanagement.GetGroupsRequest{ + Namespaces: []string{"parcoursmob_organizations"}, + }) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + for _, g := range admingroups.Groups { + groups[g.Id] = g.ToStorageType() + } + + beneficiaries, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(context.TODO(), &accounts.GetAccountsBatchRequest{ + Accountids: beneficiaries_ids, + }) + if err != nil { + log.Error().Err(err).Msg("") + w.WriteHeader(http.StatusInternalServerError) + return + } + + beneficiaries_map := map[string]accountsstorage.Account{} + for _, ben := range beneficiaries.Accounts { + beneficiaries_map[ben.Id] = ben.ToStorageType() + } + + /////////////// Generate file + + f := excelize.NewFile() + defer func() { + if err := f.Close(); err != nil { + log.Error().Err(err).Msg("") + } + }() + + f.SetCellValue("Sheet1", "A1", "Numéro") + f.SetCellValue("Sheet1", "B1", "Type") + f.SetCellValue("Sheet1", "C1", "Gestionnaire") + f.SetCellValue("Sheet1", "D1", "Prescripteur") + f.SetCellValue("Sheet1", "E1", "Bénéficiaire") + f.SetCellValue("Sheet1", "F1", "Numéro allocataire / Pole emploi") + f.SetCellValue("Sheet1", "G1", "Début de Mise à disposition") + f.SetCellValue("Sheet1", "H1", "Fin de mise à disposition") + f.SetCellValue("Sheet1", "I1", "Début indisponibilité") + f.SetCellValue("Sheet1", "J1", "Fin indisponibilité") + f.SetCellValue("Sheet1", "K1", "Véhicule retiré") + f.SetCellValue("Sheet1", "L1", "Commentaire - Retrait véhicule") + f.SetCellValue("Sheet1", "M1", "Réservation supprimée") + f.SetCellValue("Sheet1", "N1", "Motif de la suppression") + + i := 2 + for _, b := range bookings { + vehicle := vehicles[b.Vehicleid] + if len(vehicle.Administrators) == 0 { + continue + } + admin := groups[vehicle.Administrators[0]] + + bookedby := "" + if v, ok := b.Data["booked_by"].(map[string]any); ok { + if v2, ok := v["user"].(map[string]any); ok { + if v3, ok := v2["display_name"].(string); ok { + bookedby = v3 + } + } + } + + beneficiary := beneficiaries_map[b.Driver] + adminunavailability := false + + if av, ok := b.Data["administrator_unavailability"].(bool); ok && av { + adminunavailability = true + } + + deleted := "" + v, ok := b.Data["Deleted"] + fmt.Println(v) + fmt.Println(ok) + if b.Deleted || (ok && v.(bool)) { + deleted = "DELETED" + } + + f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i), vehicle.Data["licence_plate"]) + f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i), vehicle.Type) + f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i), admin.Data["name"]) + f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i), bookedby) + f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i), fmt.Sprintf("%v %v", beneficiary.Data["first_name"], beneficiary.Data["last_name"])) + f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i), beneficiary.Data["file_number"]) + f.SetCellValue("Sheet1", fmt.Sprintf("G%d", i), b.Startdate.Format("2006-01-02")) + f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i), b.Enddate.Format("2006-01-02")) + f.SetCellValue("Sheet1", fmt.Sprintf("I%d", i), b.Unavailablefrom.Format("2006-01-02")) + f.SetCellValue("Sheet1", fmt.Sprintf("J%d", i), b.Unavailableto.Format("2006-01-02")) + f.SetCellValue("Sheet1", fmt.Sprintf("K%d", i), adminunavailability) + f.SetCellValue("Sheet1", fmt.Sprintf("L%d", i), b.Data["comment"]) + f.SetCellValue("Sheet1", fmt.Sprintf("M%d", i), deleted) + f.SetCellValue("Sheet1", fmt.Sprintf("N%d", i), b.Data["motif"]) + i = i + 1 + } + + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename="+"Workbook.xlsx") + w.Header().Set("Content-Transfer-Encoding", "binary") + w.Header().Set("Expires", "0") + f.Write(w) } diff --git a/main.go b/main.go index bf6fd58..3071797 100755 --- a/main.go +++ b/main.go @@ -8,23 +8,24 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/gorilla/mux" + "git.coopgo.io/coopgo-apps/parcoursmob/handlers/api" + "git.coopgo.io/coopgo-apps/parcoursmob/handlers/api/protected" "git.coopgo.io/coopgo-apps/parcoursmob/handlers/application" "git.coopgo.io/coopgo-apps/parcoursmob/handlers/auth" "git.coopgo.io/coopgo-apps/parcoursmob/handlers/exports" "git.coopgo.io/coopgo-apps/parcoursmob/renderer" "git.coopgo.io/coopgo-apps/parcoursmob/services" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + "git.coopgo.io/coopgo-apps/parcoursmob/utils/protectapi" cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" - "github.com/gorilla/mux" ) func main() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - cfg, err := ReadConfig() if err != nil { - log.Fatal().Err(err).Msg("cannot read config") + log.Fatal().Err(err).Msg("cannot read config!") return } @@ -37,6 +38,8 @@ func main() { if dev_env { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + } else { + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix } svc, err := services.NewServicesHandler(cfg) @@ -61,9 +64,10 @@ func main() { } apiHandler, _ := api.NewAPIHandler(cfg, idp, svc, kv) + protectedApiHandler, _ := protected.NewProtectedAPIHandler(cfg, idp, svc, kv) applicationHandler, _ := application.NewApplicationHandler(cfg, svc, kv, filestorage, emailing) exportsHandler, _ := exports.NewExportsHandler(cfg, svc, emailing) - authHandler, _ := auth.NewAuthHandler(cfg, idp, svc, kv, emailing) + authHandler, _ := auth.NewAuthHandler(cfg, idp, svc, kv, emailing, filestorage) r := mux.NewRouter() @@ -85,6 +89,9 @@ func main() { calendars_router.HandleFunc("/global.ics", apiHandler.CalendarGlobal) calendars_router.HandleFunc("/organizations/{groupid}.ics", apiHandler.CalendarOrganizations) + ext_router := r.PathPrefix("/ext").Subrouter() + ext_router.HandleFunc("/st/bp/{bookingid}", applicationHandler.SolidarityTransportExternalBookingProposal) + api_router := r.PathPrefix("/api").Subrouter() api_router.HandleFunc("/", apiHandler.NotFound) api_router.HandleFunc("/geo/autocomplete", apiHandler.GeoAutocomplete) @@ -92,15 +99,25 @@ func main() { api_router.HandleFunc("/cache/{cacheid}/export", apiHandler.CacheExport) api_router.HandleFunc("/oauth2/callback", apiHandler.OAuth2Callback) + protected_api_router := api_router.PathPrefix("/protected").Subrouter() + protected_api_router.HandleFunc("/users", protectedApiHandler.Users) + protected_api_router.Use(protectapi.ApiKey(cfg.GetString("services.api.api_key"))) + + interact_router := r.PathPrefix("/int").Subrouter() + interact_router.HandleFunc("/", apiHandler.NotFound) + application := r.PathPrefix("/app").Subrouter() application.HandleFunc("/", applicationHandler.Dashboard) application.HandleFunc("/beneficiaries/", applicationHandler.BeneficiariesList) application.HandleFunc("/beneficiaries/create", applicationHandler.BeneficiaryCreate) application.HandleFunc("/beneficiaries/{beneficiaryid}", applicationHandler.BeneficiaryDisplay) application.HandleFunc("/beneficiaries/{beneficiaryid}/update", applicationHandler.BeneficiaryUpdate) + application.HandleFunc("/beneficiaries/{beneficiaryid}/archive", applicationHandler.BeneficiaryArchive) + application.HandleFunc("/beneficiaries/{beneficiaryid}/unarchive", applicationHandler.BeneficiaryUnarchive) application.HandleFunc("/beneficiaries/{beneficiaryid}/documents", applicationHandler.BeneficiaryDocuments) application.HandleFunc("/beneficiaries/{beneficiaryid}/documents/{document}", applicationHandler.BeneficiaryDocumentDownload) application.HandleFunc("/beneficiaries/{beneficiaryid}/picture", applicationHandler.BeneficiaryPicture) + application.HandleFunc("/wallets/{userid}/credit", applicationHandler.CreditWallet) application.HandleFunc("/members/{beneficiaryid}/picture", applicationHandler.BeneficiaryPicture) application.HandleFunc("/members/{adminid}", applicationHandler.MemberDisplay) application.HandleFunc("/members/{adminid}/update", applicationHandler.MemberUpdate) @@ -108,9 +125,30 @@ func main() { application.HandleFunc("/journeys/", applicationHandler.JourneysSearch) application.HandleFunc("/solidarity-transport/", applicationHandler.SolidarityTransportOverview) application.HandleFunc("/solidarity-transport/drivers/create", applicationHandler.SolidarityTransportCreateDriver) - application.HandleFunc("/solidarity-transport/drivers/{driverid}", applicationHandler.SolidarityTransportDriverDisplay) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/update", applicationHandler.SolidarityTransportUpdateDriver) application.HandleFunc("/solidarity-transport/drivers/{driverid}/availabilities", applicationHandler.SolidarityTransportAddAvailability) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/archive", applicationHandler.SolidarityTransportArchiveDriver) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/unarchive", applicationHandler.SolidarityTransportUnarchiveDriver) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/documents", applicationHandler.SolidarityTransportDriverDocuments) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/documents/{document}", applicationHandler.SolidarityTransportDocumentDownload) application.HandleFunc("/solidarity-transport/drivers/{driverid}/availabilities/{availabilityid}/delete", applicationHandler.SolidarityTransportDeleteAvailability) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/journeys/{journeyid}", applicationHandler.SolidarityTransportDriverJourney) + application.HandleFunc("/solidarity-transport/drivers/{driverid}/journeys/{journeyid}/noreturn", applicationHandler.SolidarityTransportDriverJourneyToggleNoreturn) + application.HandleFunc("/solidarity-transport/drivers/{driverid}", applicationHandler.SolidarityTransportDriverDisplay) + application.HandleFunc("/solidarity-transport/bookings/{bookingid}", applicationHandler.SolidarityTransportBookingDisplay) + application.HandleFunc("/solidarity-transport/bookings/{bookingid}/confirm", applicationHandler.SolidarityTransportBookingStatus("confirm")) + application.HandleFunc("/solidarity-transport/bookings/{bookingid}/cancel", applicationHandler.SolidarityTransportBookingStatus("cancel")) + application.HandleFunc("/solidarity-transport/bookings/{bookingid}/waitconfirmation", applicationHandler.SolidarityTransportBookingStatus("waitconfirmation")) + application.HandleFunc("/organized-carpool/", applicationHandler.OrganizedCarpoolOverview) + application.HandleFunc("/organized-carpool/drivers/create", applicationHandler.OrganizedCarpoolCreateDriver) + application.HandleFunc("/organized-carpool/drivers/{driverid}/trips", applicationHandler.OrganizedCarpoolAddTrip) + application.HandleFunc("/organized-carpool/drivers/{driverid}/archive", applicationHandler.OrganizedCarpoolArchiveDriver) + application.HandleFunc("/organized-carpool/drivers/{driverid}/unarchive", applicationHandler.OrganizedCarpoolUnarchiveDriver) + application.HandleFunc("/organized-carpool/drivers/{driverid}/documents", applicationHandler.OrganizedCarpoolDriverDocuments) + application.HandleFunc("/organized-carpool/drivers/{driverid}/documents/{document}", applicationHandler.OrganizedCarpoolDocumentDownload) + application.HandleFunc("/organized-carpool/drivers/{driverid}/trips/{tripid}/delete", applicationHandler.OrganizedCarpoolDeleteTrip) + application.HandleFunc("/organized-carpool/drivers/{driverid}", applicationHandler.OrganizedCarpoolDriverDisplay) + application.HandleFunc("/organized-carpool/drivers/{driverid}/journeys/{journeyid}", applicationHandler.OrganizedCarpoolJourney) application.HandleFunc("/vehicles/", applicationHandler.VehiclesSearch) application.HandleFunc("/vehicles/bookings/", applicationHandler.VehiclesBookingsList) application.HandleFunc("/vehicles/bookings/{bookingid}", applicationHandler.VehicleBookingDisplay) @@ -144,17 +182,15 @@ func main() { application.HandleFunc("/group/settings", applicationHandler.GroupSettingsDisplay) application.HandleFunc("/group/settings/invite-member", applicationHandler.GroupSettingsInviteMember) - /****************************Groupe Déplacement ************************************/ application.HandleFunc("/journeys/groups_covoiturage", applicationHandler.GroupsGestion) application.HandleFunc("/journeys/groups_covoiturage/create", applicationHandler.CreateGroup) application.HandleFunc("/journeys/groups_covoiturage/create/{groupid}", applicationHandler.DisplayGroupCovoiturage) application.HandleFunc("/journeys/groups_covoiturage/create/{id}/{groupid}/{memberid}", applicationHandler.UpdateGroupCovoiturage) - /****************************************************************/ - /********************Code Supprt Emailing************************/ + application.HandleFunc("/sms/send", applicationHandler.SendSMS) + application.HandleFunc("/support/", applicationHandler.SupportSend) - /*********************** CODE GROUP **************************/ appGroup := application.PathPrefix("/group_module").Subrouter() appGroup.HandleFunc("/", applicationHandler.Groups) appGroup.HandleFunc("/groups", applicationHandler.CreateGroupModule) @@ -211,10 +247,9 @@ func main() { application.HandleFunc("/diags/{diagid}", applicationHandler.DiagsDisplayDiag) application.HandleFunc("/diags/{diagid}/update", applicationHandler.DiagUpdate) application.HandleFunc("/diags/{diagid}/delete", applicationHandler.DiagDelete) - // application.HandleFunc("/diags/history", applicationHandler.DiagsHistory) export := r.PathPrefix("/exports").Subrouter() - export.HandleFunc("/fleets/bookings", exportsHandler.Bookings) + export.HandleFunc("/fleets/bookings", exportsHandler.AllBookings) export.HandleFunc("/fleets/bookings/{groupid}", exportsHandler.Bookings) export.HandleFunc("/agenda/subscriptions", exportsHandler.Agenda("allEvents")) export.HandleFunc("/agenda/{eventid}", exportsHandler.Agenda("oneEvent")) @@ -224,7 +259,7 @@ func main() { srv := &http.Server{ Handler: r, Addr: address, - WriteTimeout: 15 * time.Second, + WriteTimeout: 30 * time.Second, ReadTimeout: 15 * time.Second, } diff --git a/renderer/beneficiaries.go b/renderer/beneficiaries.go index 14d0d7c..08f3b43 100755 --- a/renderer/beneficiaries.go +++ b/renderer/beneficiaries.go @@ -53,18 +53,19 @@ type BeneficiariesDisplayState struct { Beneficiary any } -func (renderer *Renderer) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request, beneficiary any, bookings []fleetsstorage.Booking, organizations []any, beneficiaries_file_types []string, file_types_map map[string]string, documents any, event interface{}, diags []any) { +func (renderer *Renderer) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request, beneficiary any, bookings []fleetsstorage.Booking, organizations []any, beneficiaries_file_types []string, file_types_map map[string]string, documents any, event interface{}, diags []any, solidarityTransportStats any) { files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.display.files") state := NewState(r, renderer.ThemeConfig, beneficiariesMenu) state.ViewState = map[string]any{ - "beneficiary": beneficiary, - "bookings": bookings, - "beneficiaries_file_types": beneficiaries_file_types, - "file_types_map": file_types_map, - "documents": documents, - "organizations": organizations, - "event": event, - "diags": diags, + "beneficiary": beneficiary, + "bookings": bookings, + "beneficiaries_file_types": beneficiaries_file_types, + "file_types_map": file_types_map, + "documents": documents, + "organizations": organizations, + "event": event, + "diags": diags, + "solidarity_transport_stats": solidarityTransportStats, } renderer.Render("beneficiaries_display", w, r, files, state) } diff --git a/renderer/func-maps.go b/renderer/func-maps.go index 3c5b005..c3deabc 100755 --- a/renderer/func-maps.go +++ b/renderer/func-maps.go @@ -8,19 +8,44 @@ import ( "strings" "time" + groupsstorage "git.coopgo.io/coopgo-platform/groups-management/storage" + "github.com/rs/zerolog/log" + "github.com/spf13/viper" "gitlab.scity.coop/maas/navitia-golang/types" ) -func TimeFrom(d any) *time.Time { +func ModuleAvailable(group groupsstorage.Group, configmodules *viper.Viper) func(string) bool { + return func(module string) bool { + if module == "dashboard" { + return true + } + groupmodules := group.Data["modules"].(map[string]any) + modAvailable, ok := groupmodules[module].(bool) + if ok && modAvailable && configmodules.GetBool(fmt.Sprintf("modules.%s.enabled", module)) { + return true + } + return false + } +} +func TimeFrom(d any) *time.Time { + paris, err := time.LoadLocation("Europe/Paris") if date, ok := d.(time.Time); ok { - return &date + if err != nil { + return &date + } + nd := date.In(paris) + return &nd } else if date, ok := d.(string); ok { datetime, err := time.Parse("2006-01-02T15:04:05Z", date) if err != nil { - panic(err) + datetime, err = time.Parse("2006-01-02", date) + if err != nil { + log.Error().Err(err).Msg("cannot parse date") + } } - return &datetime + dt := datetime.In(paris) + return &dt } return nil } @@ -78,7 +103,6 @@ func RawJSON(v any) string { return "" } return strings.TrimSuffix(buf.String(), "\n") - } func UnescapeHTML(s string) template.HTML { @@ -127,3 +151,14 @@ func strval(v interface{}) string { func Divide[V int | float64](a, b V) V { return a / b } + +func ShortDuration(d time.Duration) string { + s := d.String() + if strings.HasSuffix(s, "m0s") { + s = s[:len(s)-2] + } + if strings.HasSuffix(s, "h0m") { + s = s[:len(s)-2] + } + return s +} diff --git a/renderer/journeys.go b/renderer/journeys.go index 7a4ee18..a470557 100755 --- a/renderer/journeys.go +++ b/renderer/journeys.go @@ -7,6 +7,7 @@ import ( groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage" mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" + geojson "github.com/paulmach/orb/geojson" ) const journeysMenu = "journeys" @@ -35,7 +36,7 @@ func (s BeneficiariesCovoiturage) JSONWithLimits(a int, b int) template.JS { return s.JSON() } -func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request, carpools any, transitjourneys any, vehicles []any, searched bool, departure any, destination any, departuredate string, departuretime string, solidarityDrivers any) { +func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request, carpools []*geojson.FeatureCollection, transitjourneys any, vehicles any, searched bool, departure any, destination any, departuredate string, departuretime string, driverJourneys any, solidarityDrivers any, organizedCarpools any, beneficiaries any, kbData any) { files := renderer.ThemeConfig.GetStringSlice("views.journeys.search.files") state := NewState(r, renderer.ThemeConfig, journeysMenu) state.ViewState = map[string]any{ @@ -46,8 +47,13 @@ func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request, "destination": destination, "journeys": transitjourneys, "carpools": carpools, + "organized_carpools": organizedCarpools, "vehicles": vehicles, + "driver_journeys": driverJourneys, "solidarity_drivers": solidarityDrivers, + "querystring": r.URL.RawQuery, + "beneficiaries": beneficiaries, + "kb_data": kbData, } renderer.Render("journeys", w, r, files, state) diff --git a/renderer/layout.go b/renderer/layout.go index 27ecffc..b723eed 100755 --- a/renderer/layout.go +++ b/renderer/layout.go @@ -2,9 +2,14 @@ package renderer type LayoutState struct { AdministrationState AdministrationState - MenuItems []MenuItem + Menu any + ActiveMenu string + + // DEPRECATED + MenuItems []MenuItem } +// DEPRECATED type MenuItem struct { Title string Link string diff --git a/renderer/organized-carpool.go b/renderer/organized-carpool.go new file mode 100644 index 0000000..0437a4f --- /dev/null +++ b/renderer/organized-carpool.go @@ -0,0 +1,61 @@ +package renderer + +import ( + "net/http" + + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" +) + +const organizedCarpoolMenu = "organized_carpool" + +func (renderer *Renderer) OrganizedCarpoolOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any) { + files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.overview.files") + state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu) + state.ViewState = map[string]any{ + "drivers": drivers, + "drivers_map": driversMap, + "passengers_map": passengersMap, + "bookings": bookings, + } + + renderer.Render("organized carpool overview", w, r, files, state) +} + +func (renderer *Renderer) OrganizedCarpoolCreateDriver(w http.ResponseWriter, r *http.Request) { + files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.driver_create.files") + state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu) + state.ViewState = map[string]any{} + + renderer.Render("organized carpool driver creation", w, r, files, state) +} + +func (renderer *Renderer) OrganizedCarpoolDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, trips any, documents any) { + files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.driver_display.files") + state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu) + + drivers_file_types := renderer.GlobalConfig.GetStringSlice("modules.organized_carpool.drivers.documents_types") + file_types_map := renderer.GlobalConfig.GetStringMapString("storage.files.file_types") + + state.ViewState = map[string]any{ + "driver": driver, + "trips": trips, + "documents": documents, + "drivers_file_types": drivers_file_types, + "file_types_map": file_types_map, + } + + renderer.Render("organized carpool driver display", w, r, files, state) +} + +func (renderer *Renderer) OrganizedCarpoolJourney(w http.ResponseWriter, r *http.Request, journey any, driver any, passenger any, beneficiaries any) { + files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.journey.files") + state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) + state.ViewState = map[string]any{ + "driver": driver, + "passenger": passenger, + "beneficiaries": beneficiaries, + "journey": journey, + } + + renderer.Render("organized carpool journey", w, r, files, state) +} diff --git a/renderer/renderer.go b/renderer/renderer.go index a9ac641..571984b 100755 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -8,6 +8,9 @@ import ( "git.coopgo.io/coopgo-apps/parcoursmob/utils/icons" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" + cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + validatedprofile "git.coopgo.io/coopgo-apps/parcoursmob/utils/validated-profile" "git.coopgo.io/coopgo-platform/emailing" "git.coopgo.io/coopgo-platform/groups-management/storage" "github.com/coreos/go-oidc/v3/oidc" @@ -20,9 +23,10 @@ type Renderer struct { GlobalConfig *viper.Viper ThemeConfig *viper.Viper Mailer *emailing.Mailer + FileStorage cache.FileStorage } -func NewRenderer(global *viper.Viper, templates_dir string) *Renderer { +func NewRenderer(global *viper.Viper, templates_dir string, filestorage cache.FileStorage) *Renderer { theme := viper.New() theme.SetConfigName("config") theme.AddConfigPath(templates_dir) @@ -34,6 +38,7 @@ func NewRenderer(global *viper.Viper, templates_dir string) *Renderer { TemplatesDir: templates_dir, GlobalConfig: global, ThemeConfig: theme, + FileStorage: filestorage, } } @@ -51,24 +56,41 @@ func (renderer *Renderer) Render(name string, w http.ResponseWriter, r *http.Req w.WriteHeader(http.StatusOK) t := template.New(name).Funcs( template.FuncMap{ - "timeFrom": TimeFrom, - "timeFormat": TimeFormat, - "genderISO5218": GenderISO5218, - "dict": Dict, - "json": JSON, - "rawjson": RawJSON, - "unescapeHTML": UnescapeHTML, - "walkingLength": WalkingLength, - "divideFloat64": Divide[float64], - "divideInt": Divide[int], - "typeOf": reflect.TypeOf, + "moduleAvailable": ModuleAvailable(state.Group, renderer.GlobalConfig), + "timeFrom": TimeFrom, + "timeFormat": TimeFormat, + "genderISO5218": GenderISO5218, + "dict": Dict, + "json": JSON, + "rawjson": RawJSON, + "unescapeHTML": UnescapeHTML, + "walkingLength": WalkingLength, + "divideFloat64": Divide[float64], + "divideInt": Divide[int], + "typeOf": reflect.TypeOf, + "shortDuration": ShortDuration, + "beneficiaryValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.beneficiaries.validated_profile")), + "solidarityDriverValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.solidarity_transport.drivers.validated_profile")), + "carpoolDriverValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.organized_carpool.drivers.validated_profile")), + "beneficiaryDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + id) + return documents + }, + "solidarityDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_SOLIDARITY_TRANSPORT_DRIVERS + "/" + id) + return documents + }, + "carpoolDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_ORGANIZED_CARPOOL_DRIVERS + "/" + id) + return documents + }, }, ) t = template.Must(t.ParseFiles(prefixed_files...)) err := t.ExecuteTemplate(w, "main", state) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("issue executing template") } } @@ -81,23 +103,39 @@ func (renderer *Renderer) RenderNoLayout(name string, w http.ResponseWriter, r * w.WriteHeader(http.StatusOK) t := template.New(name).Funcs( template.FuncMap{ - "timeFrom": TimeFrom, - "timeFormat": TimeFormat, - "genderISO5218": GenderISO5218, - "dict": Dict, - "json": JSON, - "rawjson": RawJSON, - "unsescapeHTML": UnescapeHTML, - "divideFloat64": Divide[float64], - "divideInt": Divide[int], - "typeOf": reflect.TypeOf, + "timeFrom": TimeFrom, + "timeFormat": TimeFormat, + "genderISO5218": GenderISO5218, + "dict": Dict, + "json": JSON, + "rawjson": RawJSON, + "unsescapeHTML": UnescapeHTML, + "divideFloat64": Divide[float64], + "divideInt": Divide[int], + "typeOf": reflect.TypeOf, + "shortDuration": ShortDuration, + "beneficiaryValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.beneficiaries.validated_profile")), + "solidarityDriverValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.solidarity_transport.drivers.validated_profile")), + "carpoolDriverValidatedProfile": validatedprofile.ValidateProfile(renderer.GlobalConfig.Sub("modules.organized_carpool.drivers.validated_profile")), + "beneficiaryDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + id) + return documents + }, + "solidarityDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_SOLIDARITY_TRANSPORT_DRIVERS + "/" + id) + return documents + }, + "carpoolDocuments": func(id string) []filestorage.FileInfo { + documents := renderer.FileStorage.List(filestorage.PREFIX_ORGANIZED_CARPOOL_DRIVERS + "/" + id) + return documents + }, }, ) t = template.Must(t.ParseFiles(prefixed_files...)) err := t.ExecuteTemplate(w, "main", state) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("issue executing template") } } @@ -117,6 +155,7 @@ type RenderState struct { func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) RenderState { iconset := themeConfig.GetStringMapString("icons.svg") + menu := themeConfig.Get("menu_items") // Get State elements from Request var userid string @@ -162,6 +201,9 @@ func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) Rende Active: menuState == administrationMenu, }, + Menu: menu, + + // DEPRECATED MenuItems: []MenuItem{ { Title: "Tableau de bord", @@ -172,6 +214,7 @@ func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) Rende }, } + // DEPRECATED if modules["beneficiaries"] != nil && modules["beneficiaries"].(bool) { ls.MenuItems = append(ls.MenuItems, MenuItem{ Title: "Bénéficiaires", @@ -252,14 +295,6 @@ func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) Rende Icon: "hero:outline/document-text", }) } - /*if modules["conseillers"] != nil && modules["conseillers"].(bool) { - ls.MenuItems = append(ls.MenuItems, MenuItem{ - Title: "Conseillers", - Link: "/app/conseillers/", - Active: menuState == membersMenu, - Icon: "hero:outline/user-group", - }) - }*/ /*if modules["diags"] != nil && modules["diags"].(bool) { ls.MenuItems = append(ls.MenuItems, MenuItem{ Title: "Diagnostics", diff --git a/renderer/solidarity-transport-ext.go b/renderer/solidarity-transport-ext.go new file mode 100644 index 0000000..315af40 --- /dev/null +++ b/renderer/solidarity-transport-ext.go @@ -0,0 +1,16 @@ +package renderer + +import "net/http" + +func (renderer *Renderer) SolidarityTransportExternalBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, driver any, passenger any) { + files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.ext.booking_proposal.files") + state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) + state.ViewState = map[string]any{ + "driver": driver, + "passenger": passenger, + "booking": booking, + "config": renderer.GlobalConfig, + } + + renderer.RenderNoLayout("booking display", w, r, files, state) +} diff --git a/renderer/solidarity-transport.go b/renderer/solidarity-transport.go index 00e914b..2a5a390 100644 --- a/renderer/solidarity-transport.go +++ b/renderer/solidarity-transport.go @@ -8,11 +8,15 @@ import ( const solidarityTransportMenu = "solidarity_transport" -func (renderer *Renderer) SolidarityTransportOverview(w http.ResponseWriter, r *http.Request, drivers []mobilityaccountsstorage.Account) { +func (renderer *Renderer) SolidarityTransportOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any, bookingsHistory any) { files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.overview.files") state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) state.ViewState = map[string]any{ - "drivers": drivers, + "drivers": drivers, + "drivers_map": driversMap, + "passengers_map": passengersMap, + "bookings": bookings, + "bookings_history": bookingsHistory, } renderer.Render("solidarity transport overview", w, r, files, state) @@ -26,13 +30,58 @@ func (renderer *Renderer) SolidarityTransportCreateDriver(w http.ResponseWriter, renderer.Render("solidarity transport driver creation", w, r, files, state) } -func (renderer *Renderer) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account) { - files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_display.files") +func (renderer *Renderer) SolidarityTransportUpdateDriver(w http.ResponseWriter, r *http.Request, driver any) { + files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_update.files") state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) state.ViewState = map[string]any{ - "driver": driver, - "documents": []any{}, + "driver": driver, + } + + renderer.Render("solidarity transport driver update", w, r, files, state) +} + +func (renderer *Renderer) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, availabilities any, documents any, bookings any, stats map[string]any) { + files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_display.files") + state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) + + drivers_file_types := renderer.GlobalConfig.GetStringSlice("modules.solidarity_transport.drivers.documents_types") + file_types_map := renderer.GlobalConfig.GetStringMapString("storage.files.file_types") + + state.ViewState = map[string]any{ + "driver": driver, + "availabilities": availabilities, + "bookings": bookings, + "documents": documents, + "drivers_file_types": drivers_file_types, + "file_types_map": file_types_map, + "stats": stats, } renderer.Render("solidarity transport driver creation", w, r, files, state) } + +func (renderer *Renderer) SolidarityTransportDriverJourney(w http.ResponseWriter, r *http.Request, driverJourney any, driver any, passenger any, beneficiaries any) { + files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_journey.files") + state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) + state.ViewState = map[string]any{ + "driver": driver, + "passenger": passenger, + "beneficiaries": beneficiaries, + "driver_journey": driverJourney, + "config": renderer.GlobalConfig, + } + + renderer.Render("solidarity transport driver creation", w, r, files, state) +} + +func (renderer *Renderer) SolidarityTransportBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, driver any, passenger any) { + files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.booking_display.files") + state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu) + state.ViewState = map[string]any{ + "driver": driver, + "passenger": passenger, + "booking": booking, + } + + renderer.Render("booking display", w, r, files, state) +} diff --git a/renderer/vehicles.go b/renderer/vehicles.go index 6c9f829..80d1fa6 100755 --- a/renderer/vehicles.go +++ b/renderer/vehicles.go @@ -5,6 +5,7 @@ import ( filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" "git.coopgo.io/coopgo-platform/fleets/storage" + fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage" mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" ) @@ -22,7 +23,7 @@ func selectDocumentsDefaults(beneficiarydocuments []filestorage.FileInfo, mandat return res } -func (renderer *Renderer) VehiclesSearch(w http.ResponseWriter, r *http.Request, beneficiaries []mobilityaccountsstorage.Account, searched bool, vehicles []any, beneficiary any, startdate any, enddate any, mandatory_documents []string, file_types_map map[string]string, beneficiarydocuments []filestorage.FileInfo, selected_type string, automatic bool, vehicles_types []string, admingroups map[string]any) { +func (renderer *Renderer) VehiclesSearch(w http.ResponseWriter, r *http.Request, beneficiaries []mobilityaccountsstorage.Account, searched bool, vehicles []fleetsstorage.Vehicle, beneficiary any, startdate any, enddate any, mandatory_documents []string, file_types_map map[string]string, beneficiarydocuments []filestorage.FileInfo, selected_type string, automatic bool, vehicles_types []string, admingroups map[string]any) { files := renderer.ThemeConfig.GetStringSlice("views.vehicles.search.files") state := NewState(r, renderer.ThemeConfig, vehiclesMenu) viewstate := map[string]any{ diff --git a/services/carpool.go b/services/carpool.go new file mode 100644 index 0000000..61af535 --- /dev/null +++ b/services/carpool.go @@ -0,0 +1,23 @@ +package services + +import ( + "git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto" + "google.golang.org/grpc" +) + +type CarpoolService struct { + proto.CarpoolServiceClient +} + +func NewCarpoolService(dial string) (*CarpoolService, error) { + conn, err := grpc.Dial(dial, grpc.WithInsecure()) + + client := proto.NewCarpoolServiceClient(conn) + if err != nil { + return nil, err + } + + return &CarpoolService{ + CarpoolServiceClient: client, + }, nil +} diff --git a/services/mobilityaccounts.go b/services/mobilityaccounts.go index 57384d6..5bde04e 100755 --- a/services/mobilityaccounts.go +++ b/services/mobilityaccounts.go @@ -91,6 +91,37 @@ func (s *ServicesHandler) GetAccountsInNamespace(namespace string) (accounts []s return } +func (s *ServicesHandler) GetAccountsInNamespaceMap(namespace string) (accounts map[string]storage.Account, err error) { + accounts = map[string]storage.Account{} + request := &mobilityaccounts.GetAccountsRequest{ + Namespaces: []string{namespace}, + } + resp, err := s.GRPC.MobilityAccounts.GetAccounts(context.TODO(), request) + + if err == nil { + for _, v := range resp.Accounts { + accounts[v.Id] = v.ToStorageType() + } + } + + return +} + +func (s *ServicesHandler) GetAccountsInNamespacesMap(namespaces []string) (accounts map[string]storage.Account, err error) { + accounts = map[string]storage.Account{} + request := &mobilityaccounts.GetAccountsRequest{ + Namespaces: namespaces, + } + resp, err := s.GRPC.MobilityAccounts.GetAccounts(context.TODO(), request) + + if err == nil { + for _, v := range resp.Accounts { + accounts[v.Id] = v.ToStorageType() + } + } + + return +} func (s *ServicesHandler) GetAccount(id string) (account storage.Account, err error) { request := &mobilityaccounts.GetAccountRequest{ diff --git a/services/services.go b/services/services.go index 50af6bb..7cab2dd 100755 --- a/services/services.go +++ b/services/services.go @@ -2,32 +2,53 @@ package services import ( agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi" + carpoolservice "git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto" diags "git.coopgo.io/coopgo-platform/diags/grpcapi" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" + "git.coopgo.io/coopgo-platform/geography/handlers/admin" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" + multimodal "git.coopgo.io/coopgo-platform/multimodal-routing/handlers" + "git.coopgo.io/coopgo-platform/multimodal-routing/libs/transit/motis" + "git.coopgo.io/coopgo-platform/payments/pricing" + "git.coopgo.io/coopgo-platform/routing-service" + "git.coopgo.io/coopgo-platform/sms" + solidaritytransport "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) type ServicesHandler struct { - GRPC GRPCServices + GRPC GRPCServices + InteropCarpool *multimodal.CarpoolRoutingHandler + Routing routing.RoutingService + TransitRouting *motis.ClientWithResponses + SMS *sms.SMSHandler + Pricing pricing.PricingService + Geography admin.AdminIndex } type GRPCServices struct { - MobilityAccounts mobilityaccounts.MobilityAccountsClient - GroupsManagement groupsmanagement.GroupsManagementClient - Fleets fleets.FleetsClient - Agenda agenda.AgendaClient - Diags diags.DiagsClient + MobilityAccounts mobilityaccounts.MobilityAccountsClient + GroupsManagement groupsmanagement.GroupsManagementClient + Fleets fleets.FleetsClient + Agenda agenda.AgendaClient + SolidarityTransport solidaritytransport.SolidarityTransportClient + CarpoolService carpoolservice.CarpoolServiceClient + Diags diags.DiagsClient } func NewServicesHandler(cfg *viper.Viper) (*ServicesHandler, error) { var ( - mobilityAccountsDial = cfg.GetString("services.grpc.mobilityaccounts.dial") - groupsManagementDial = cfg.GetString("services.grpc.groupsmanagement.dial") - fleetsDial = cfg.GetString("services.grpc.fleets.dial") - agendaDial = cfg.GetString("services.grpc.agenda.dial") + mobilityAccountsDial = cfg.GetString("services.grpc.mobilityaccounts.dial") + groupsManagementDial = cfg.GetString("services.grpc.groupsmanagement.dial") + fleetsDial = cfg.GetString("services.grpc.fleets.dial") + agendaDial = cfg.GetString("services.grpc.agenda.dial") + solidarityTransportDial = cfg.GetString("services.grpc.solidaritytransport.dial") + carpoolServiceDial = cfg.GetString("services.grpc.carpoolservice.dial") + routing_service_type = cfg.GetString("routing.type") + valhalla_base_url = cfg.GetString("routing.valhalla.base_url") + // diagsDial = cfg.GetString("services.grpc.diags.dial") ) mobilityAccounts, err := NewMobilityAccountService(mobilityAccountsDial) @@ -37,20 +58,31 @@ func NewServicesHandler(cfg *viper.Viper) (*ServicesHandler, error) { } groupsManagement, err := NewGroupsManagementService(groupsManagementDial) - log.Error().Err(err).Msg("Groups mgmt service issue") if err != nil { + log.Error().Err(err).Msg("Groups mgmt service issue") return nil, err } fleetsSvc, err := NewFleetsService(fleetsDial) - log.Error().Err(err).Msg("Fleets service issue") if err != nil { + log.Error().Err(err).Msg("Fleets service issue") return nil, err } agendaSvc, err := NewAgendaService(agendaDial) - log.Error().Err(err).Msg("Agenda service issue") if err != nil { + log.Error().Err(err).Msg("Agenda service issue") + return nil, err + } + + solidarityTransportSvc, err := NewSolidarityTransportService(solidarityTransportDial) + if err != nil { + log.Error().Err(err).Msg("Solidarity Transport service issue") + return nil, err + } + carpoolSvc, err := NewCarpoolService(carpoolServiceDial) + if err != nil { + log.Error().Err(err).Msg("Carpool service service issue") return nil, err } @@ -59,14 +91,61 @@ func NewServicesHandler(cfg *viper.Viper) (*ServicesHandler, error) { //if err != nil { // return nil, err //} + routing, err := routing.NewRoutingService(routing_service_type, valhalla_base_url) + if err != nil { + log.Fatal().Err(err).Msg("Could not initiate the routing service") + return nil, err + } + + carpoolRouting, err := multimodal.NewCarpoolRoutingHandler(cfg.Sub("multimodal.modes.carpool")) + if err != nil { + log.Fatal().Err(err).Msg("Could not initiate the carpool routing service") + return nil, err + } + + motisRouting, err := motis.NewClientWithResponses(cfg.GetString("multimodal.modes.transit.motis.server")) + if err != nil { + log.Fatal().Err(err).Msg("Could not initiate the transit routing service") + return nil, err + } + + pricing_type := cfg.GetString("payments.pricing.type") + if pricing_type == "" { + pricing_type = "mms43" + } + pricingService, err := pricing.NewPricingService(pricing_type) + if err != nil { + log.Fatal().Err(err).Msg("Could not initiate the pricing service") + return nil, err + } + + smsHandler, err := sms.NewSMSHandler(cfg.Sub("sms")) + if err != nil { + log.Fatal().Err(err).Msg("Could not initiate the SMS handler") + return nil, err + } + + geography, err := admin.NewAdminIndex(cfg.Sub("geography")) + if err != nil { + log.Fatal().Err(err).Msg("could not initiate admin index") + return nil, err + } return &ServicesHandler{ GRPC: GRPCServices{ - MobilityAccounts: mobilityAccounts, - GroupsManagement: groupsManagement, - Fleets: fleetsSvc, - Agenda: agendaSvc, + MobilityAccounts: mobilityAccounts, + GroupsManagement: groupsManagement, + Fleets: fleetsSvc, + Agenda: agendaSvc, + SolidarityTransport: solidarityTransportSvc, + CarpoolService: carpoolSvc, // Diags: diagsSvc, }, + Routing: routing, + InteropCarpool: carpoolRouting, + TransitRouting: motisRouting, + SMS: smsHandler, + Pricing: pricingService, + Geography: geography, }, nil } diff --git a/services/solidaritytransport.go b/services/solidaritytransport.go new file mode 100644 index 0000000..59d9b00 --- /dev/null +++ b/services/solidaritytransport.go @@ -0,0 +1,23 @@ +package services + +import ( + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" + "google.golang.org/grpc" +) + +type SolidarityTransportService struct { + gen.SolidarityTransportClient +} + +func NewSolidarityTransportService(dial string) (*SolidarityTransportService, error) { + conn, err := grpc.Dial(dial, grpc.WithInsecure()) + + client := gen.NewSolidarityTransportClient(conn) + if err != nil { + return nil, err + } + + return &SolidarityTransportService{ + SolidarityTransportClient: client, + }, nil +} diff --git a/utils/protectapi/api_key.go b/utils/protectapi/api_key.go new file mode 100644 index 0000000..19f24c2 --- /dev/null +++ b/utils/protectapi/api_key.go @@ -0,0 +1,19 @@ +package protectapi + +import ( + "net/http" + + "github.com/rs/zerolog/log" +) + +func ApiKey(apiKey string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if apiKey == "" || r.Header.Get("X_API_KEY") != apiKey { + log.Info().Msg("call to api not allowed") + w.WriteHeader(http.StatusForbidden) + } + next.ServeHTTP(w, r) + }) + } +} diff --git a/utils/sorting/fleets.go b/utils/sorting/fleets.go index c973585..ca1536f 100755 --- a/utils/sorting/fleets.go +++ b/utils/sorting/fleets.go @@ -1,9 +1,15 @@ package sorting import ( + "cmp" + "encoding/json" "strings" + "git.coopgo.io/coopgo-platform/fleets/storage" fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage" + "github.com/paulmach/orb/geo" + "github.com/paulmach/orb/geojson" + "github.com/rs/zerolog/log" ) type VehiclesByLicencePlate []fleetsstorage.Vehicle @@ -21,3 +27,47 @@ func (a BookingsByStartdate) Less(i, j int) bool { return a[i].Startdate.Before(a[j].Startdate) } func (a BookingsByStartdate) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// Functions + +func VehiclesByDistanceFrom(from geojson.Feature) func(vehicle1, vehicle2 storage.Vehicle) int { + return func(vehicle1, vehicle2 storage.Vehicle) int { + vehicle1Address, ok := vehicle1.Data["address"] + if !ok { + return 1 + } + vehicle1Json, err := json.Marshal(vehicle1Address) + if err != nil { + log.Error().Err(err).Msg("failed marshalling vehicle 1 json") + return 1 + } + + vehicle1Geojson, err := geojson.UnmarshalFeature(vehicle1Json) + if err != nil { + log.Error().Err(err).Msg("failed unmarshalling vehicle 1 geojson") + return 1 + } + + vehicle2Address, ok := vehicle2.Data["address"] + if !ok { + log.Debug().Msg("Vehicle 2 does not have an address") + return -1 + } + vehicle2Json, err := json.Marshal(vehicle2Address) + if err != nil { + log.Error().Err(err).Msg("failed marshalling vehicle 2 json") + return -1 + } + + vehicle2Geojson, err := geojson.UnmarshalFeature(vehicle2Json) + if err != nil { + log.Error().Err(err).Msg("failed unmarshalling vehicle 2 geojson") + return -1 + } + + distance1 := geo.Distance(from.Point(), vehicle1Geojson.Point()) + distance2 := geo.Distance(from.Point(), vehicle2Geojson.Point()) + + return cmp.Compare(distance1, distance2) + } +} diff --git a/utils/sorting/solidarity-drivers.go b/utils/sorting/solidarity-drivers.go deleted file mode 100644 index 287b316..0000000 --- a/utils/sorting/solidarity-drivers.go +++ /dev/null @@ -1,13 +0,0 @@ -package sorting - -import ( - mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" -) - -type SolidarityDriversByName []mobilityaccountsstorage.Account - -func (e SolidarityDriversByName) Len() int { return len(e) } -func (e SolidarityDriversByName) Less(i, j int) bool { - return e[i].Data["first_name"].(string) < e[j].Data["first_name"].(string) -} -func (e SolidarityDriversByName) Swap(i, j int) { e[i], e[j] = e[j], e[i] } diff --git a/utils/sorting/solidarity-transport.go b/utils/sorting/solidarity-transport.go new file mode 100644 index 0000000..8ee4de8 --- /dev/null +++ b/utils/sorting/solidarity-transport.go @@ -0,0 +1,32 @@ +package sorting + +import ( + "strings" + + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" + "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen" +) + +type SolidarityDriversByName []mobilityaccountsstorage.Account + +func (e SolidarityDriversByName) Len() int { return len(e) } +func (e SolidarityDriversByName) Less(i, j int) bool { + return e[i].Data["first_name"].(string) < e[j].Data["first_name"].(string) +} +func (e SolidarityDriversByName) Swap(i, j int) { e[i], e[j] = e[j], e[i] } + +type SolidarityAvailabilitiesByDay []*gen.DriverRegularAvailability + +func (e SolidarityAvailabilitiesByDay) Len() int { return len(e) } +func (e SolidarityAvailabilitiesByDay) Less(i, j int) bool { + if e[i].Day == e[j].Day { + return strings.Compare(e[i].StartTime, e[j].StartTime) < 0 + } + + if e[i].Day == 0 { + return false + } + + return e[i].Day < e[j].Day +} +func (e SolidarityAvailabilitiesByDay) Swap(i, j int) { e[i], e[j] = e[j], e[i] } diff --git a/utils/storage/files.go b/utils/storage/files.go index 168b08c..a4e12f7 100755 --- a/utils/storage/files.go +++ b/utils/storage/files.go @@ -8,10 +8,12 @@ import ( ) const ( - PREFIX_BENEFICIARIES = "beneficiaries" - PREFIX_BOOKINGS = "fleets_bookings" - PREFIX_AGENDA = "event_files" - PREFIX_DIAGS = "diags" + PREFIX_BENEFICIARIES = "beneficiaries" + PREFIX_SOLIDARITY_TRANSPORT_DRIVERS = "solidarity_transport/drivers" + PREFIX_ORGANIZED_CARPOOL_DRIVERS = "organized_carpool/drivers" + PREFIX_BOOKINGS = "fleets_bookings" + PREFIX_AGENDA = "event_files" + PREFIX_DIAGS = "diags" ) type FileInfo struct { diff --git a/utils/validated-profile/validated-profile.go b/utils/validated-profile/validated-profile.go new file mode 100644 index 0000000..ef18a94 --- /dev/null +++ b/utils/validated-profile/validated-profile.go @@ -0,0 +1,102 @@ +package validatedprofile + +import ( + "cmp" + "slices" + "time" + + "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" + mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage" + "github.com/go-viper/mapstructure/v2" + "github.com/rs/zerolog/log" + "github.com/spf13/viper" + "github.com/stretchr/objx" +) + +type Comparison struct { + Field string + Type string + Value any +} + +func ValidateProfile(cfg *viper.Viper) func(mobilityaccountsstorage.Account, []storage.FileInfo) bool { + enabled := cfg.GetBool("enabled") + requiredDocuments := cfg.GetStringSlice("required.documents") + requiredFields := cfg.GetStringSlice("required.fields") + comp := cfg.Get("assert.compare") + var comparisons []Comparison + err := mapstructure.Decode(comp, &comparisons) + if err != nil { + log.Error().Err(err).Msg("reading comparisons issue") + } + return func(account mobilityaccountsstorage.Account, docs []storage.FileInfo) bool { + if !enabled { + return true + } + + for _, d := range requiredDocuments { + if !slices.ContainsFunc(docs, func(f storage.FileInfo) bool { + log.Debug().Str("required", d).Str("checked", f.Metadata["Type"]).Msg("file check") + return f.Metadata["Type"] == d + }) { + log.Debug().Msg("file missing") + return false + } + } + + obj := objx.Map(account.Data) + + for _, f := range requiredFields { + if obj.Get(f) == nil { + log.Debug().Msg("field missing") + return false + } + } + + for _, c := range comparisons { + val := obj.Get(c.Field) + if val == nil { + return false + } + value := "" + if v, ok := c.Value.(string); ok { + value = v + } else if v, ok := c.Value.(time.Time); ok { + value = v.Format("2006-01-02") + } else { + log.Error().Msg("could not get type") + return false + } + result := cmp.Compare(val.String(), value) + + if c.Type == "gte" { + if result < 0 { + log.Debug().Int("comparison result", result).Str("operand", c.Type).Msg("comparison issue") + return false + } + } else if c.Type == "gt" { + if result <= 0 { + log.Debug().Int("comparison result", result).Str("operand", c.Type).Msg("comparison issue") + return false + } + } else if c.Type == "lt" { + if result >= 0 { + log.Debug().Int("comparison result", result).Str("operand", c.Type).Msg("comparison issue") + return false + } + } else if c.Type == "lte" { + if result < 0 { + log.Debug().Int("comparison result", result).Str("operand", c.Type).Msg("comparison issue") + return false + } + } else if c.Type == "eq" { + if result != 0 { + log.Debug().Int("comparison result", result).Str("operand", c.Type).Msg("comparison issue") + return false + } + } + } + + return true + } +}