mirror of
https://github.com/1f349/lavender.git
synced 2024-12-22 07:34:06 +00:00
Start merging lavender and tulip
This commit is contained in:
parent
a6c0751061
commit
a81aa0458a
@ -2,6 +2,7 @@ package conf
|
||||
|
||||
import (
|
||||
"github.com/1f349/lavender/issuer"
|
||||
"github.com/1f349/lavender/mail"
|
||||
)
|
||||
|
||||
type Conf struct {
|
||||
@ -10,5 +11,7 @@ type Conf struct {
|
||||
ServiceName string `yaml:"serviceName"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
Kid string `yaml:"kid"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
Mail mail.Mail `yaml:"mail"`
|
||||
SsoServices []issuer.SsoConfig `yaml:"ssoServices"`
|
||||
}
|
||||
|
12
go.mod
12
go.mod
@ -6,8 +6,12 @@ require (
|
||||
github.com/1f349/cache v0.0.3
|
||||
github.com/1f349/mjwt v0.4.1
|
||||
github.com/1f349/overlapfs v0.0.1
|
||||
github.com/1f349/tulip v0.0.0-20240725211619-6b19e2d4ca63
|
||||
github.com/charmbracelet/log v0.4.0
|
||||
github.com/cloudflare/tableflip v1.2.3
|
||||
github.com/emersion/go-message v0.18.1
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43
|
||||
github.com/emersion/go-smtp v0.21.3
|
||||
github.com/go-oauth2/oauth2/v4 v4.5.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||
@ -17,7 +21,9 @@ require (
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/text v0.17.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@ -31,7 +37,7 @@ require (
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
@ -53,10 +59,8 @@ require (
|
||||
github.com/tidwall/rtred v0.1.2 // indirect
|
||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
)
|
||||
|
39
go.sum
39
go.sum
@ -7,6 +7,8 @@ github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0
|
||||
github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o=
|
||||
github.com/1f349/rsa-helper v0.0.2 h1:N/fLQqg5wrjIzG6G4zdwa5Xcv9/jIPutCls9YekZr9U=
|
||||
github.com/1f349/rsa-helper v0.0.2/go.mod h1:VUQ++1tYYhYrXeOmVFkQ82BegR24HQEJHl5lHbjg7yg=
|
||||
github.com/1f349/tulip v0.0.0-20240725211619-6b19e2d4ca63 h1:jPg+0bgKD5kY7yQtRZqeba+BGKFE51evGvwewZwa7Xc=
|
||||
github.com/1f349/tulip v0.0.0-20240725211619-6b19e2d4ca63/go.mod h1:1zFQhcbgiyPSWHVMp0cXJjmd6FhasP5bf5tWS4ZK61A=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@ -27,6 +29,13 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E=
|
||||
github.com/emersion/go-message v0.18.1/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
|
||||
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
@ -72,8 +81,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@ -193,29 +202,40 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -233,19 +253,30 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/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-20220722155257-8c9f86f7a55f/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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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/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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
|
104
lists/locales.go
Normal file
104
lists/locales.go
Normal file
@ -0,0 +1,104 @@
|
||||
package lists
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/language/display"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
localeOnce sync.Once
|
||||
localeNames []struct{ Value, Label string }
|
||||
)
|
||||
|
||||
func ListLocale() []struct{ Value, Label string } {
|
||||
localeOnce.Do(func() {
|
||||
localeNames = make([]struct{ Value, Label string }, len(localeList))
|
||||
for i := range localeList {
|
||||
localeNames[i] = struct{ Value, Label string }{Value: localeList[i].String(), Label: display.Self.Name(localeList[i])}
|
||||
}
|
||||
})
|
||||
return localeNames
|
||||
}
|
||||
|
||||
var localeList = []language.Tag{
|
||||
language.Afrikaans,
|
||||
language.Amharic,
|
||||
language.Arabic,
|
||||
language.ModernStandardArabic,
|
||||
language.Azerbaijani,
|
||||
language.Bulgarian,
|
||||
language.Bengali,
|
||||
language.Catalan,
|
||||
language.Czech,
|
||||
language.Danish,
|
||||
language.German,
|
||||
language.Greek,
|
||||
language.English,
|
||||
language.AmericanEnglish,
|
||||
language.BritishEnglish,
|
||||
language.Spanish,
|
||||
language.EuropeanSpanish,
|
||||
language.LatinAmericanSpanish,
|
||||
language.Estonian,
|
||||
language.Persian,
|
||||
language.Finnish,
|
||||
language.Filipino,
|
||||
language.French,
|
||||
language.CanadianFrench,
|
||||
language.Gujarati,
|
||||
language.Hebrew,
|
||||
language.Hindi,
|
||||
language.Croatian,
|
||||
language.Hungarian,
|
||||
language.Armenian,
|
||||
language.Indonesian,
|
||||
language.Icelandic,
|
||||
language.Italian,
|
||||
language.Japanese,
|
||||
language.Georgian,
|
||||
language.Kazakh,
|
||||
language.Khmer,
|
||||
language.Kannada,
|
||||
language.Korean,
|
||||
language.Kirghiz,
|
||||
language.Lao,
|
||||
language.Lithuanian,
|
||||
language.Latvian,
|
||||
language.Macedonian,
|
||||
language.Malayalam,
|
||||
language.Mongolian,
|
||||
language.Marathi,
|
||||
language.Malay,
|
||||
language.Burmese,
|
||||
language.Nepali,
|
||||
language.Dutch,
|
||||
language.Norwegian,
|
||||
language.Punjabi,
|
||||
language.Polish,
|
||||
language.Portuguese,
|
||||
language.BrazilianPortuguese,
|
||||
language.EuropeanPortuguese,
|
||||
language.Romanian,
|
||||
language.Russian,
|
||||
language.Sinhala,
|
||||
language.Slovak,
|
||||
language.Slovenian,
|
||||
language.Albanian,
|
||||
language.Serbian,
|
||||
language.SerbianLatin,
|
||||
language.Swedish,
|
||||
language.Swahili,
|
||||
language.Tamil,
|
||||
language.Telugu,
|
||||
language.Thai,
|
||||
language.Turkish,
|
||||
language.Ukrainian,
|
||||
language.Urdu,
|
||||
language.Uzbek,
|
||||
language.Vietnamese,
|
||||
language.Chinese,
|
||||
language.SimplifiedChinese,
|
||||
language.TraditionalChinese,
|
||||
language.Zulu,
|
||||
}
|
15
lists/locales_test.go
Normal file
15
lists/locales_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package lists
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListLocale(t *testing.T) {
|
||||
locales := ListLocale()
|
||||
assert.True(t, len(locales) > 4)
|
||||
assert.Equal(t, struct{ Value, Label string }{Value: "af", Label: "Afrikaans"}, locales[0])
|
||||
assert.Equal(t, struct{ Value, Label string }{Value: "am", Label: "አማርኛ"}, locales[1])
|
||||
assert.Equal(t, struct{ Value, Label string }{Value: "zh-Hant", Label: "繁體中文"}, locales[len(locales)-2])
|
||||
assert.Equal(t, struct{ Value, Label string }{Value: "zu", Label: "isiZulu"}, locales[len(locales)-1])
|
||||
}
|
53
lists/zoneinfo.go
Normal file
53
lists/zoneinfo.go
Normal file
@ -0,0 +1,53 @@
|
||||
package lists
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
zoneDirs = []string{
|
||||
// Update path according to your OS
|
||||
"/usr/share/zoneinfo/",
|
||||
"/usr/share/lib/zoneinfo/",
|
||||
"/usr/lib/locale/TZ/",
|
||||
}
|
||||
zoneInfoOnce sync.Once
|
||||
zoneNames []string
|
||||
)
|
||||
|
||||
func ListZoneInfo() []string {
|
||||
zoneInfoOnce.Do(func() {
|
||||
zoneNames = make([]string, 0)
|
||||
for _, zoneDir := range zoneDirs {
|
||||
zoneNames = append(zoneNames, FindTimeZoneFiles(zoneDir)...)
|
||||
}
|
||||
sort.Strings(zoneNames)
|
||||
})
|
||||
return zoneNames
|
||||
}
|
||||
|
||||
func FindTimeZoneFiles(zoneDir string) []string {
|
||||
dArr := make([]string, 0)
|
||||
dArr = append(dArr, "")
|
||||
arr := make([]string, 0)
|
||||
|
||||
for i := 0; i < len(dArr); i++ {
|
||||
dir := dArr[i]
|
||||
files, _ := os.ReadDir(filepath.Join(zoneDir, dir))
|
||||
for _, f := range files {
|
||||
if f.Name() != strings.ToUpper(f.Name()[:1])+f.Name()[1:] {
|
||||
continue
|
||||
}
|
||||
if f.IsDir() {
|
||||
dArr = append(dArr, filepath.Join(dir, f.Name()))
|
||||
} else {
|
||||
arr = append(arr, filepath.Join(dir, f.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
15
lists/zoneinfo_test.go
Normal file
15
lists/zoneinfo_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package lists
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListZoneInfo(t *testing.T) {
|
||||
zoneinfos := ListZoneInfo()
|
||||
assert.True(t, len(zoneinfos) > 4)
|
||||
assert.Equal(t, "Africa/Abidjan", zoneinfos[0])
|
||||
assert.Equal(t, "Africa/Accra", zoneinfos[1])
|
||||
assert.Equal(t, "WET", zoneinfos[len(zoneinfos)-2])
|
||||
assert.Equal(t, "Zulu", zoneinfos[len(zoneinfos)-1])
|
||||
}
|
26
mail/from-address.go
Normal file
26
mail/from-address.go
Normal file
@ -0,0 +1,26 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
type FromAddress struct {
|
||||
*mail.Address
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = &FromAddress{}
|
||||
|
||||
func (f *FromAddress) UnmarshalJSON(b []byte) error {
|
||||
var a string
|
||||
err := json.Unmarshal(b, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
address, err := mail.ParseAddress(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Address = address
|
||||
return nil
|
||||
}
|
96
mail/mail.go
Normal file
96
mail/mail.go
Normal file
@ -0,0 +1,96 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/emersion/go-message/mail"
|
||||
"github.com/emersion/go-sasl"
|
||||
"github.com/emersion/go-smtp"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Mail struct {
|
||||
Name string `json:"name"`
|
||||
Tls bool `json:"tls"`
|
||||
Server string `json:"server"`
|
||||
From FromAddress `json:"from"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (m *Mail) loginInfo() sasl.Client {
|
||||
return sasl.NewPlainClient("", m.Username, m.Password)
|
||||
}
|
||||
|
||||
func (m *Mail) mailCall(to []string, r io.Reader) error {
|
||||
host, _, err := net.SplitHostPort(m.Server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.Tls {
|
||||
return smtp.SendMailTLS(m.Server, m.loginInfo(), m.From.String(), to, r)
|
||||
}
|
||||
if host == "localhost" || host == "127.0.0.1" {
|
||||
// internals of smtp.SendMail without STARTTLS for localhost testing
|
||||
dial, err := smtp.Dial(m.Server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dial.Auth(m.loginInfo())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dial.SendMail(m.From.String(), to, r)
|
||||
}
|
||||
return smtp.SendMail(m.Server, m.loginInfo(), m.From.String(), to, r)
|
||||
}
|
||||
|
||||
func (m *Mail) SendMail(subject string, to []*mail.Address, htmlBody, textBody io.Reader) error {
|
||||
// generate the email in this template
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// setup mail headers
|
||||
var h mail.Header
|
||||
h.SetDate(time.Now())
|
||||
h.SetSubject(subject)
|
||||
h.SetAddressList("From", []*mail.Address{m.From.Address})
|
||||
h.SetAddressList("To", to)
|
||||
h.Set("Content-Type", "multipart/alternative")
|
||||
|
||||
// setup html and text alternative headers
|
||||
var hHtml, hTxt mail.InlineHeader
|
||||
hHtml.Set("Content-Type", "text/html; charset=utf-8")
|
||||
hTxt.Set("Content-Type", "text/plain; charset=utf-8")
|
||||
|
||||
createWriter, err := mail.CreateWriter(buf, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inline, err := createWriter.CreateInline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
partHtml, err := inline.CreatePart(hHtml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(partHtml, htmlBody); err != nil {
|
||||
return err
|
||||
}
|
||||
partTxt, err := inline.CreatePart(hTxt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(partTxt, textBody); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert all to addresses to strings
|
||||
toStr := make([]string, len(to))
|
||||
for i := range toStr {
|
||||
toStr[i] = to[i].String()
|
||||
}
|
||||
|
||||
return m.mailCall(toStr, buf)
|
||||
}
|
18
mail/send-template.go
Normal file
18
mail/send-template.go
Normal file
@ -0,0 +1,18 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/1f349/lavender/mail/templates"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func (m *Mail) SendEmailTemplate(templateName, subject, nameOfUser string, to *mail.Address, data map[string]any) error {
|
||||
var bufHtml, bufTxt bytes.Buffer
|
||||
templates.RenderMailTemplate(&bufHtml, &bufTxt, templateName, map[string]any{
|
||||
"ServiceName": m.Name,
|
||||
"Name": nameOfUser,
|
||||
"Data": data,
|
||||
})
|
||||
return m.SendMail(fmt.Sprintf("%s - %s", subject, m.Name), []*mail.Address{to}, &bufHtml, &bufTxt)
|
||||
}
|
10
mail/templates/mail-account-delete.go.html
Normal file
10
mail/templates/mail-account-delete.go.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Hello, {{.Name}}</p>
|
||||
<p>Your account with {{.ServiceName}} has been disabled and marked for deletion.</p>
|
||||
<p>Your account will be fully deleted within 48-hours.</p>
|
||||
<p>You will no longer receive emails from {{.ServiceName}}, unless your email address is used to set up an account.</p>
|
||||
<p>Regards,<br/>{{.ServiceName}}</p>
|
||||
</body>
|
||||
</html>
|
10
mail/templates/mail-account-delete.go.txt
Normal file
10
mail/templates/mail-account-delete.go.txt
Normal file
@ -0,0 +1,10 @@
|
||||
Hello, {{.Name}}
|
||||
|
||||
Your account with {{.ServiceName}} has been disabled and marked for deletion.
|
||||
|
||||
Your account will be fully deleted within 48-hours.
|
||||
|
||||
You will no longer receive emails from {{.ServiceName}}, unless your email address is used to set up an account.
|
||||
|
||||
Regards,
|
||||
{{.ServiceName}}
|
11
mail/templates/mail-register-admin.go.html
Normal file
11
mail/templates/mail-register-admin.go.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Hello, {{.Name}}</p>
|
||||
<p>Your email address has been registered with {{.ServiceName}} by an administrator.</p>
|
||||
<p>Please open this link to verify your email address and <a href="{{.Data.RegisterUrl}}" target="_blank">register your account</a></p>
|
||||
<p>If you did not wish to register for {{.ServiceName}}, then your email has probably been used by mistake.</p>
|
||||
<p>If the link above is not used to register an account, then no further contact will be made from {{.ServiceName}} and your email address will be deleted from our systems within a 48-hour period.</p>
|
||||
<p>Regards,<br/>{{.ServiceName}}</p>
|
||||
</body>
|
||||
</html>
|
12
mail/templates/mail-register-admin.go.txt
Normal file
12
mail/templates/mail-register-admin.go.txt
Normal file
@ -0,0 +1,12 @@
|
||||
Hello, {{.Name}}
|
||||
|
||||
Your email address has been registered with {{.ServiceName}} by an administrator.
|
||||
|
||||
Please open this link to verify your email address and register your account {{.Data.RegisterUrl}}
|
||||
|
||||
If you did not wish to register for {{.ServiceName}}, then your email has probably been used by mistake.
|
||||
|
||||
If the link above is not used to register an account, then no further contact will be made from {{.ServiceName}} and your email address will be deleted from our systems within a 48-hour period.
|
||||
|
||||
Regards,
|
||||
{{.ServiceName}}
|
9
mail/templates/mail-reset-password.go.html
Normal file
9
mail/templates/mail-reset-password.go.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Hello, {{.Name}}</p>
|
||||
<p>Please open this link to <a href="{{.Data.ResetUrl}}" target="_blank">reset your password</a></p>
|
||||
<p>This link is valid for 10 minutes.</p>
|
||||
<p>Regards,<br/>{{.ServiceName}}</p>
|
||||
</body>
|
||||
</html>
|
8
mail/templates/mail-reset-password.go.txt
Normal file
8
mail/templates/mail-reset-password.go.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Hello, {{.Name}}
|
||||
|
||||
Please open this link to reset your password: {{.Data.ResetUrl}}
|
||||
|
||||
This link is valid for 10 minutes.
|
||||
|
||||
Regards,
|
||||
{{.ServiceName}}
|
10
mail/templates/mail-verify.go.html
Normal file
10
mail/templates/mail-verify.go.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Hello, {{.Name}}</p>
|
||||
<p>Please open this link to <a href="{{.Data.VerifyUrl}}" target="_blank">verify your email address</a></p>
|
||||
<p>This link is valid for 10 minutes.</p>
|
||||
<p>If you did not create an account with {{.ServiceName}} then please ignore this email and the account will be deleted within a 48-hour period.</p>
|
||||
<p>Regards,<br/>{{.ServiceName}}</p>
|
||||
</body>
|
||||
</html>
|
10
mail/templates/mail-verify.go.txt
Normal file
10
mail/templates/mail-verify.go.txt
Normal file
@ -0,0 +1,10 @@
|
||||
Hello, {{.Name}}
|
||||
|
||||
Please open this link to verify your email address: {{.Data.VerifyUrl}}
|
||||
|
||||
This link is valid for 10 minutes.
|
||||
|
||||
If you did not create an account with {{.ServiceName}} then please ignore this email and the account will be deleted within a 48-hour period.
|
||||
|
||||
Regards,
|
||||
{{.ServiceName}}
|
55
mail/templates/templates.go
Normal file
55
mail/templates/templates.go
Normal file
@ -0,0 +1,55 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"github.com/1f349/overlapfs"
|
||||
"github.com/1f349/tulip/logger"
|
||||
htmlTemplate "html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
textTemplate "text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed *.go.html *.go.txt
|
||||
embeddedTemplates embed.FS
|
||||
mailHtmlTemplates *htmlTemplate.Template
|
||||
mailTextTemplates *textTemplate.Template
|
||||
loadOnce sync.Once
|
||||
)
|
||||
|
||||
func LoadMailTemplates(wd string) (err error) {
|
||||
loadOnce.Do(func() {
|
||||
var o fs.FS = embeddedTemplates
|
||||
if wd != "" {
|
||||
mailDir := filepath.Join(wd, "mail-templates")
|
||||
err = os.Mkdir(mailDir, os.ModePerm)
|
||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||
return
|
||||
}
|
||||
wdFs := os.DirFS(mailDir)
|
||||
o = overlapfs.OverlapFS{A: embeddedTemplates, B: wdFs}
|
||||
}
|
||||
mailHtmlTemplates, err = htmlTemplate.New("mail").ParseFS(o, "*.go.html")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mailTextTemplates, err = textTemplate.New("mail").ParseFS(o, "*.go.txt")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func RenderMailTemplate(wrHtml, wrTxt io.Writer, name string, data any) {
|
||||
err := mailHtmlTemplates.ExecuteTemplate(wrHtml, name+".go.html", data)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to render mail html", "name", name, "err", err)
|
||||
}
|
||||
err = mailTextTemplates.ExecuteTemplate(wrTxt, name+".go.txt", data)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to render mail text", "name", name, "err", err)
|
||||
}
|
||||
}
|
17
password/password.go
Normal file
17
password/password.go
Normal file
@ -0,0 +1,17 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// HashString is used to represent a string containing a password hash
|
||||
type HashString string
|
||||
|
||||
func HashPassword(password string) (HashString, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
return HashString(bytes), err
|
||||
}
|
||||
|
||||
func CheckPasswordHash(hash HashString, password string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
}
|
Loading…
Reference in New Issue
Block a user