Add_Directory_and_ProfilePicture #9
|
@ -47,11 +47,6 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/**session, err := store.Get(r, SESSION_NAME)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}**/
|
|
||||||
|
|
||||||
//Search values with ldap and filter
|
//Search values with ldap and filter
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
config.UserBaseDN,
|
config.UserBaseDN,
|
||||||
|
|
6
go.mod
|
@ -7,8 +7,12 @@ require (
|
||||||
github.com/emersion/go-smtp v0.12.1
|
github.com/emersion/go-smtp v0.12.1
|
||||||
github.com/go-ldap/ldap v3.0.3+incompatible
|
github.com/go-ldap/ldap v3.0.3+incompatible
|
||||||
github.com/go-ldap/ldap/v3 v3.1.6
|
github.com/go-ldap/ldap/v3 v3.1.6
|
||||||
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/gorilla/sessions v1.2.0
|
github.com/gorilla/sessions v1.2.0
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/minio/minio-go/v7 v7.0.0
|
||||||
|
github.com/sirupsen/logrus v1.6.0
|
||||||
|
github.com/stretchr/objx v0.1.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
|
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
)
|
)
|
||||||
|
|
43
go.sum
|
@ -1,4 +1,6 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
|
@ -10,25 +12,66 @@ github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHj
|
||||||
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||||
github.com/go-ldap/ldap/v3 v3.1.6 h1:VTihvB7egSAvU6KOagaiA/EvgJMR2jsjRAVIho2ydBo=
|
github.com/go-ldap/ldap/v3 v3.1.6 h1:VTihvB7egSAvU6KOagaiA/EvgJMR2jsjRAVIho2ydBo=
|
||||||
github.com/go-ldap/ldap/v3 v3.1.6/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
|
github.com/go-ldap/ldap/v3 v3.1.6/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
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/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
||||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||||
|
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
|
||||||
|
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.0 h1:99hRCmsmMi+hKK93C26iPnRQebTsdK8GEx8Xb4XLr7I=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.0/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns=
|
||||||
|
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||||
|
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
|
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
|
||||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||||
|
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
|
8
main.go
|
@ -43,6 +43,10 @@ type ConfigFile struct {
|
||||||
AdminAccount string `json:"admin_account"`
|
AdminAccount string `json:"admin_account"`
|
||||||
GroupCanInvite string `json:"group_can_invite"`
|
GroupCanInvite string `json:"group_can_invite"`
|
||||||
GroupCanAdmin string `json:"group_can_admin"`
|
GroupCanAdmin string `json:"group_can_admin"`
|
||||||
|
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
erwan marked this conversation as resolved
Outdated
|
|||||||
|
AccesKey string `json:"acces_key"`
|
||||||
|
SecretKey string `json:"secret_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var configFlag = flag.String("config", "./config.json", "Configuration file path")
|
var configFlag = flag.String("config", "./config.json", "Configuration file path")
|
||||||
|
@ -133,6 +137,8 @@ func main() {
|
||||||
r.HandleFunc("/profile", handleProfile)
|
r.HandleFunc("/profile", handleProfile)
|
||||||
r.HandleFunc("/passwd", handlePasswd)
|
r.HandleFunc("/passwd", handlePasswd)
|
||||||
|
|
||||||
|
r.HandleFunc("/image/{name}/{size}", handleDownloadImage)
|
||||||
|
|
||||||
r.HandleFunc("/directory", handleDirectory)
|
r.HandleFunc("/directory", handleDirectory)
|
||||||
r.HandleFunc("/search/{input}", handleSearch)
|
r.HandleFunc("/search/{input}", handleSearch)
|
||||||
|
|
||||||
|
@ -244,7 +250,7 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus {
|
||||||
login_info.DN,
|
login_info.DN,
|
||||||
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
requestKind,
|
requestKind,
|
||||||
[]string{"dn", "displayname", "givenname", "sn", "mail", "memberof", "visibility", "description"},
|
[]string{"dn", "displayname", "givenname", "sn", "mail", "memberof", "visibility", "description", "profilImage"},
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
pas de français ! appller ça pas de français ! appller ça `profilePicture` et non `profilImage`
|
|||||||
nil)
|
nil)
|
||||||
|
|
||||||
sr, err := l.Search(searchRequest)
|
sr, err := l.Search(searchRequest)
|
||||||
|
|
244
mimioClient.go
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
package main
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
il faudrait renommer ce fichier, moi je l'appellerais juste il faudrait renommer ce fichier, moi je l'appellerais juste `s3.go`
|
|||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"github.com/nfnt/resize"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Upload image through guichet server.
|
||||||
|
func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bool, string, error) {
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
Pourquoi on renvoie un bool, un string et une error ? je pense qu'on peut enlever le bool, ça complexifie trop et on se perd, et utiliser la sémantique suivante pour
Il suffit ensuite de checker qu'il n'y a pas d'erreur, puis copier la valeur dans LDAP seulement si elle n'est pas Pourquoi on renvoie un bool, un string et une error ? je pense qu'on peut enlever le bool, ça complexifie trop et on se perd, et utiliser la sémantique suivante pour `(string, error)`:
- `("", nil)`: aucun fichier n'a été envoyé par l'utilisateur, il ne se passe rien
- `("", une erreur)`: une erreur se produit lors du traitement
- `("machintruc.jpg", nil)`: on a traîté l'image correctement et elle existe sur s3
Il suffit ensuite de checker qu'il n'y a pas d'erreur, puis copier la valeur dans LDAP seulement si elle n'est pas `""`
|
|||||||
|
file, _, err := r.FormFile("image")
|
||||||
|
|
||||||
|
if err == http.ErrMissingFile {
|
||||||
|
return false, "", nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
fileType, err := checkImage(file)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
if fileType == "" {
|
||||||
|
return false, "", nil
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
1. a-t-on vraiment besoin de ce check? car si on arrive ici on a `err == nil`, et le seul cas où c'est possible c'est quand le filetype est un filetype `image/` correct
|
|||||||
|
}
|
||||||
|
|
||||||
|
buff := bytes.NewBuffer([]byte{})
|
||||||
|
buff_thumbnail := bytes.NewBuffer([]byte{})
|
||||||
|
err = resizeThumb(file, buff, buff_thumbnail)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mc, err := newMimioClient()
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
Minio, et pas Mimio ;) Minio, et pas Mimio ;)
|
|||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
if mc == nil {
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
tu peux mettre les deux tu peux mettre les deux `if` en un seul avec `||`
|
|||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var name, nameFull string
|
||||||
|
|
||||||
|
if nameConsul := login.UserEntry.GetAttributeValue("profilImage"); nameConsul != "" {
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
et ensuite dans le code tu utilises toujours la constante, tu ne répète jamais `profilePicture` au lieu de `profilImage`. Pour être sûr d'avoir la même valeur que partout, tu peux définir ça dans une constante:
`const PROFILE_PICTURE_FIELD_NAME = "profilePicture"`
et ensuite dans le code tu utilises toujours la constante, tu ne répète jamais `"profilePicture"`
|
|||||||
|
name = nameConsul
|
||||||
|
nameFull = "full_" + name
|
||||||
|
} else {
|
||||||
|
name = uuid.New().String() + ".jpeg"
|
||||||
|
nameFull = "full_" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = mc.PutObject(context.Background(), "bottin-pictures", name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
Le nom du bucket Le nom du bucket `bottin-pictures` ça doit être dans config aussi, d'autres gens peuvent avoir un bucket qui s'appelle autrement
|
|||||||
|
ContentType: "image/jpeg",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = mc.PutObject(context.Background(), "bottin-pictures", nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{
|
||||||
|
ContentType: "image/jpeg",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMimioClient() (*minio.Client, error) {
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
Minio* Minio*
|
|||||||
|
endpoint := config.Endpoint
|
||||||
|
accessKeyID := config.AccesKey
|
||||||
|
secretKeyID := config.SecretKey
|
||||||
|
useSSL := true
|
||||||
|
|
||||||
|
//Initialize Minio
|
||||||
|
minioCLient, err := minio.New(endpoint, &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""),
|
||||||
|
Secure: useSSL,
|
||||||
|
Region: "garage",
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
La région ça devrait aussi être dans la config La région ça devrait aussi être dans la config
|
|||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return minioCLient, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkImage(file multipart.File) (string, error) {
|
||||||
|
buff := make([]byte, 512) //Detect read only the first 512 bytes
|
||||||
|
_, err := file.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
file.Seek(0, 0)
|
||||||
|
|
||||||
|
fileType := http.DetectContentType(buff)
|
||||||
|
fileType = strings.Split(fileType, "/")[0]
|
||||||
|
switch fileType {
|
||||||
|
case "image":
|
||||||
|
return fileType, nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("bad type")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error {
|
||||||
|
file.Seek(0, 0)
|
||||||
|
images, _, err := image.Decode(file)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Decode: " + err.Error())
|
||||||
|
}
|
||||||
|
//Encode image to jpeg a first time to eliminate all problems
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
Pas besoin du premier encodage en jpeg, tu peux le virer Pas besoin du premier encodage en jpeg, tu peux le virer
|
|||||||
|
err = jpeg.Encode(buff, images, &jpeg.Options{
|
||||||
|
Quality: 100, //Between 1 to 100, higher is better
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
images, _, err = image.Decode(buff)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buff.Reset()
|
||||||
|
images = resize.Thumbnail(200, 200, images, resize.Lanczos3)
|
||||||
|
images_thumbnail := resize.Thumbnail(80, 80, images, resize.Lanczos3)
|
||||||
lx marked this conversation as resolved
Outdated
lx
commented
Si l'image d'entrée est pas carrée, qu'est-ce que ça fait ? Si l'image d'entrée est pas carrée, qu'est-ce que ça fait ?
erwan
commented
J'ai testé avec des screenshots/ wallpapers et il n'y a eu aucun problèmes. ca les transforme en 200x196 le plus souvent. Si la taille est déjà inférieure à 200 alors il n'y touche pas. J'ai testé avec des screenshots/ wallpapers et il n'y a eu aucun problèmes. ca les transforme en 200x196 le plus souvent. Si la taille est déjà inférieure à 200 alors il n'y touche pas.
|
|||||||
|
|
||||||
|
err = jpeg.Encode(buff, images, &jpeg.Options{
|
||||||
|
Quality: 95,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jpeg.Encode(buff_thumbnail, images_thumbnail, &jpeg.Options{
|
||||||
|
Quality: 95,
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
//Get input value by user
|
||||||
|
dn := mux.Vars(r)["name"]
|
||||||
|
size := mux.Vars(r)["size"]
|
||||||
|
|
||||||
|
//Check login
|
||||||
|
login := checkLogin(w, r)
|
||||||
|
if login == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var imageName string
|
||||||
|
if dn != "unknown_profile" {
|
||||||
|
//Search values with ldap and filter
|
||||||
|
searchRequest := ldap.NewSearchRequest(
|
||||||
|
dn,
|
||||||
|
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
"(objectclass=*)",
|
||||||
|
[]string{"profilImage"},
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
`PROFILE_PICTURE_FIELD_NAME`
|
|||||||
|
nil)
|
||||||
|
|
||||||
|
sr, err := login.conn.Search(searchRequest)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Search: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(sr.Entries) != 1 {
|
||||||
|
http.Error(w, fmt.Sprintf("Not found user: %s cn: %s and numberEntries: %d", dn, strings.Split(dn, ",")[0], len(sr.Entries)), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imageName = sr.Entries[0].GetAttributeValue("profilImage")
|
||||||
|
if imageName == "" {
|
||||||
|
http.Error(w, "User doesn't have profile image", http.StatusInternalServerError)
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
ça serait plutôt une 404 Not found et pas une 500 Internal error ça serait plutôt une 404 Not found et pas une 500 Internal error
|
|||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageName = "unknown_profile.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
if size == "full" {
|
||||||
|
imageName = "full_" + imageName
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the object after connect MC
|
||||||
|
mc, err := newMimioClient()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj, err := mc.GetObject(context.Background(), "bottin-pictures", imageName, minio.GetObjectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "MinioClient: GetObject: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer obj.Close()
|
||||||
|
objStat, err := obj.Stat()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "MinioObjet: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send JSON through xhttp
|
||||||
|
w.Header().Set("Content-Type", objStat.ContentType)
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size)))
|
||||||
|
//http.Error(w, fmt.Sprintf("Length buffer: %d", objStat.Size), http.StatusInternalServerError)
|
||||||
|
buff := make([]byte, objStat.Size)
|
||||||
|
|
||||||
|
obj.Seek(0, 0)
|
||||||
|
n, err := obj.Read(buff)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
http.Error(w, fmt.Sprintf("Read Error: %s, bytes Read: %d, bytes in file: %d", err.Error(), n, objStat.Size), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if int64(n) != objStat.Size {
|
||||||
|
http.Error(w, fmt.Sprintf("Read %d bytes on %d bytes", n, objStat.Size), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Write(buff); err != nil {
|
||||||
|
http.Error(w, "WriteBody: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
erwan marked this conversation as resolved
Outdated
lx
commented
tu peux simplifier tout ça en utilisant tu peux simplifier tout ça en utilisant `io.Copy(w, obj)`, ça t'évite de gérer un buffer et de prendre le risque de te planter sur les tailles ou les offset
|
|||||||
|
}
|
25
profile.go
|
@ -18,6 +18,7 @@ type ProfileTplData struct {
|
||||||
Surname string
|
Surname string
|
||||||
Visibility string
|
Visibility string
|
||||||
Description string
|
Description string
|
||||||
|
NameImage string
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleProfile(w http.ResponseWriter, r *http.Request) {
|
func handleProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -42,13 +43,27 @@ func handleProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
data.Description = login.UserEntry.GetAttributeValue("description")
|
data.Description = login.UserEntry.GetAttributeValue("description")
|
||||||
|
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
r.ParseForm()
|
//5MB maximum size files
|
||||||
|
r.ParseMultipartForm(5 << 20)
|
||||||
|
|
||||||
data.DisplayName = strings.TrimSpace(strings.Join(r.Form["display_name"], ""))
|
data.DisplayName = strings.TrimSpace(strings.Join(r.Form["display_name"], ""))
|
||||||
data.GivenName = strings.TrimSpace(strings.Join(r.Form["given_name"], ""))
|
data.GivenName = strings.TrimSpace(strings.Join(r.Form["given_name"], ""))
|
||||||
data.Surname = strings.TrimSpace(strings.Join(r.Form["surname"], ""))
|
data.Surname = strings.TrimSpace(strings.Join(r.Form["surname"], ""))
|
||||||
data.Description = strings.Trim(strings.Join(r.Form["description"], ""), "")
|
data.Description = strings.Trim(strings.Join(r.Form["description"], ""), "")
|
||||||
data.Visibility = strings.TrimSpace(strings.Join(r.Form["visibility"], ""))
|
visible := strings.TrimSpace(strings.Join(r.Form["visibility"], ""))
|
||||||
|
if visible != "" {
|
||||||
|
visible = "on"
|
||||||
|
}
|
||||||
|
data.Visibility = visible
|
||||||
|
|
||||||
|
ok, name, err := uploadImage(w, r, login)
|
||||||
|
if err != nil {
|
||||||
|
data.ErrorMessage = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
data.NameImage = name
|
||||||
|
}
|
||||||
|
|
||||||
modify_request := ldap.NewModifyRequest(login.Info.DN, nil)
|
modify_request := ldap.NewModifyRequest(login.Info.DN, nil)
|
||||||
modify_request.Replace("displayname", []string{data.DisplayName})
|
modify_request.Replace("displayname", []string{data.DisplayName})
|
||||||
|
@ -56,13 +71,17 @@ func handleProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
modify_request.Replace("sn", []string{data.Surname})
|
modify_request.Replace("sn", []string{data.Surname})
|
||||||
modify_request.Replace("description", []string{data.Description})
|
modify_request.Replace("description", []string{data.Description})
|
||||||
modify_request.Replace("visibility", []string{data.Visibility})
|
modify_request.Replace("visibility", []string{data.Visibility})
|
||||||
|
if ok {
|
||||||
|
modify_request.Replace("profilImage", []string{data.NameImage})
|
||||||
|
}
|
||||||
|
|
||||||
err := login.conn.Modify(modify_request)
|
err = login.conn.Modify(modify_request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
data.ErrorMessage = err.Error()
|
data.ErrorMessage = err.Error()
|
||||||
} else {
|
} else {
|
||||||
data.Success = true
|
data.Success = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templateProfile.Execute(w, data)
|
templateProfile.Execute(w, data)
|
||||||
|
|
BIN
static/image/34431.png
Normal file
After Width: | Height: | Size: 764 KiB |
5
static/javascript/minio.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
document.querySelector('.custom-file-input').addEventListener('change',function(e){
|
||||||
|
var fileName = document.getElementById("image").files[0].name;
|
||||||
|
var nextSibling = e.target.nextElementSibling
|
||||||
|
nextSibling.innerText = fileName
|
||||||
|
})
|
|
@ -21,12 +21,18 @@ function searchDirectory() {
|
||||||
|
|
||||||
for (let i =0; i < Object.keys(jsonResponse.search).length; i++) {
|
for (let i =0; i < Object.keys(jsonResponse.search).length; i++) {
|
||||||
var row = table.insertRow(0);
|
var row = table.insertRow(0);
|
||||||
var identifiant = row.insertCell(0);
|
var urlName = row.insertCell(0);
|
||||||
var name = row.insertCell(1);
|
var identifiant = row.insertCell(1);
|
||||||
var email = row.insertCell(2);
|
var name = row.insertCell(2);
|
||||||
var description = row.insertCell(3);
|
var email = row.insertCell(3);
|
||||||
|
var description = row.insertCell(4);
|
||||||
description.setAttribute("style", "word-break: break-all;");
|
description.setAttribute("style", "word-break: break-all;");
|
||||||
|
|
||||||
|
if (jsonResponse.search[i].dn.localeCompare("")!=0) {
|
||||||
|
urlName.innerHTML = `<object data="/image/${jsonResponse.search[i].dn}/little" class=".img-thumbnail"><image src="/image/unknown_profile/little" class=".img-thumbnail"></object>`
|
||||||
|
}else {
|
||||||
|
urlName.innerHTML=""
|
||||||
|
}
|
||||||
identifiant.innerHTML = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].identifiant}</a>`
|
identifiant.innerHTML = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].identifiant}</a>`
|
||||||
name.innerHTML = jsonResponse.search[i].name
|
name.innerHTML = jsonResponse.search[i].name
|
||||||
email.innerHTML = jsonResponse.search[i].email
|
email.innerHTML = jsonResponse.search[i].email
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
<table class="table mt-4">
|
<table class="table mt-4">
|
||||||
<thead>
|
<thead>
|
||||||
|
<th scope="col">Profil image</th>
|
||||||
<th scope="col">Identifiant</th>
|
<th scope="col">Identifiant</th>
|
||||||
<th scope="col">Nom complet</th>
|
<th scope="col">Nom complet</th>
|
||||||
<th scope="col">Email</th>
|
<th scope="col">Email</th>
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
<h4>Modifier mon profil</h4>
|
<h4>Modifier mon profil</h4>
|
||||||
<a class="ml-auto btn btn-info" href="/">Retour</a>
|
<a class="ml-auto btn btn-info" href="/">Retour</a>
|
||||||
</div>
|
</div>
|
||||||
|
<h5>Photo de profil</h5>
|
||||||
|
<object data="/image/{{ .Status.Info.DN}}/full" class=".img-thumbnail">
|
||||||
|
<img src="/image/unknown_profile/full" alt="Stack Overflow logo and icons and such">
|
||||||
|
</object>
|
||||||
{{if .ErrorMessage}}
|
{{if .ErrorMessage}}
|
||||||
<div class="alert alert-danger mt-4">Impossible d'effectuer la modification.
|
<div class="alert alert-danger mt-4">Impossible d'effectuer la modification.
|
||||||
<div style="font-size: 0.8em">{{ .ErrorMessage }}</div>
|
<div style="font-size: 0.8em">{{ .ErrorMessage }}</div>
|
||||||
|
@ -16,7 +19,7 @@
|
||||||
Profil enregistré.
|
Profil enregistré.
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<form method="POST" class="mt-4">
|
<form method="POST" class="mt-4" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Nom d'utilisateur:</label>
|
<label>Nom d'utilisateur:</label>
|
||||||
<input type="text" disabled="true" class="form-control" value="{{ .Status.Info.Username }}" />
|
<input type="text" disabled="true" class="form-control" value="{{ .Status.Info.Username }}" />
|
||||||
|
@ -43,12 +46,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group form-check">
|
<div class="form-group form-check">
|
||||||
{{if .Visibility}}
|
{{if .Visibility}}
|
||||||
<input class="form-check-input" name="visibility" type="checkbox" id="visibility" checked>
|
<input class="form-check-input" name="visibility" type="checkbox" id="visibility" value="on" checked>
|
||||||
{{else}}
|
{{else}}
|
||||||
<input class="form-check-input" name="visibility" type="checkbox" id="visibility">
|
<input class="form-check-input" name="visibility" type="checkbox" id="visibility">
|
||||||
{{end}}
|
{{end}}
|
||||||
<label class="form-check-label" for="visibility">Apparaît sur l'annuaire</label>
|
<label class="form-check-label" for="visibility">Apparaît sur l'annuaire</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group input-group mb-3">
|
||||||
|
<div class="form-group custom-file">
|
||||||
|
<input type="file" name="image" class="custom-file-input" id="image">
|
||||||
|
<label class="custom-file-label" for="image">Choose picture (jpeg, jpg or png)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Enregistrer les modifications</button>
|
<button type="submit" class="btn btn-primary">Enregistrer les modifications</button>
|
||||||
</form>
|
</form>
|
||||||
|
<script src="/static/javascript/minio.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Endpoint de quoi? Il faudrait appeller ça
S3Endpoint
pour être clair, et les autres les appellerS3AccessKey
etS3SecretKey
.