ref project
This commit is contained in:
parent
b4ce07515c
commit
34debbdd3e
21
go.mod
21
go.mod
@ -2,11 +2,21 @@ module codeword
|
|||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/caarlos0/env/v8 v8.0.0
|
||||||
|
github.com/gofiber/fiber/v2 v2.51.0
|
||||||
|
github.com/stretchr/testify v1.8.1
|
||||||
|
go.mongodb.org/mongo-driver v1.13.1
|
||||||
|
go.uber.org/zap v1.26.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/caarlos0/env/v8 v8.0.0 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/gofiber/fiber/v2 v2.51.0 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/go-faker/faker/v4 v4.2.0 // indirect
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
github.com/google/uuid v1.4.0 // indirect
|
github.com/google/uuid v1.4.0 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
@ -14,6 +24,8 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
|
github.com/pioz/faker v1.7.3 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
@ -22,11 +34,10 @@ require (
|
|||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
go.mongodb.org/mongo-driver v1.13.1 // indirect
|
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.uber.org/zap v1.26.0 // indirect
|
|
||||||
golang.org/x/crypto v0.7.0 // indirect
|
golang.org/x/crypto v0.7.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
33
go.sum
33
go.sum
@ -2,13 +2,22 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
|
|||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
|
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
|
||||||
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/go-faker/faker/v4 v4.2.0 h1:dGebOupKwssrODV51E0zbMrv5e2gO9VWSLNC1WDCpWg=
|
||||||
|
github.com/go-faker/faker/v4 v4.2.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4=
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
||||||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -24,8 +33,20 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
|
|||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM=
|
||||||
|
github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
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/testify v1.6.1/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.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||||
@ -43,6 +64,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
|
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
|
||||||
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
||||||
|
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||||
|
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||||
@ -84,4 +107,10 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -1,13 +1,36 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/smtp"
|
||||||
|
)
|
||||||
|
|
||||||
type RecoveryEmailSender struct{}
|
type RecoveryEmailSender struct {
|
||||||
|
SmtpHost string
|
||||||
|
SmtpPort string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
ApiKey string
|
||||||
|
}
|
||||||
|
|
||||||
// SendRecoveryEmail отправляет email с подписью для восстановления доступа
|
// SendRecoveryEmail отправляет email с подписью для восстановления доступа
|
||||||
func (r *RecoveryEmailSender) SendRecoveryEmail(email, signature string) error {
|
func (r *RecoveryEmailSender) SendRecoveryEmail(email, signature string) error {
|
||||||
|
// прост как пример пока что
|
||||||
|
message := fmt.Sprintf("To: %s\r\n"+
|
||||||
|
"Subject: Восстановление доступа\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"Чтобы восстановить доступ, пожалуйста, перейдите по ссылке ниже:\r\n"+
|
||||||
|
" https://hub.pena.digital/codeword/restore/%s\r\n", email, signature)
|
||||||
|
|
||||||
fmt.Printf("Отправляем письмо для восстановления доступа на: %s с подписью: %s\n", email, signature)
|
auth := smtp.PlainAuth("", r.Username, r.Password, r.SmtpHost)
|
||||||
|
|
||||||
|
err := smtp.SendMail(r.SmtpHost+":"+r.SmtpPort, auth, r.Username, []string{email}, []byte(message))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Ошибка при отправке письма: %s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Письмо для восстановления доступа отправлено на: %s\n", email)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeword/internal/adapters/client"
|
|
||||||
controller "codeword/internal/controller/recovery"
|
controller "codeword/internal/controller/recovery"
|
||||||
"codeword/internal/initialize"
|
"codeword/internal/initialize"
|
||||||
"codeword/internal/repository"
|
"codeword/internal/repository"
|
||||||
@ -9,7 +8,6 @@ import (
|
|||||||
"codeword/internal/services"
|
"codeword/internal/services"
|
||||||
"codeword/internal/utils/encrypt"
|
"codeword/internal/utils/encrypt"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
"time"
|
||||||
@ -24,28 +22,30 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdb, err := initialize.InitializeRedis(ctx, cfg)
|
||||||
|
|
||||||
encryptService := encrypt.New(&encrypt.EncryptDeps{
|
encryptService := encrypt.New(&encrypt.EncryptDeps{
|
||||||
PublicKey: cfg.PublicCurveKey,
|
PublicKey: cfg.PublicCurveKey,
|
||||||
PrivateKey: cfg.PrivateCurveKey,
|
PrivateKey: cfg.PrivateCurveKey,
|
||||||
SignSecret: cfg.SignSecret,
|
SignSecret: cfg.SignSecret,
|
||||||
})
|
})
|
||||||
|
|
||||||
userRepo := repository.NewUserRepository(mdb)
|
codewordRepo := repository.NewCodewordRepository(repository.Deps{Rdb: rdb, Mdb: mdb.Collection("codeword")})
|
||||||
|
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("users")})
|
||||||
|
|
||||||
recoveryEmailSender := &client.RecoveryEmailSender{}
|
//recoveryEmailSender := &client.RecoveryEmailSender{}
|
||||||
|
|
||||||
recoveryService := services.NewRecoveryService(services.Deps{
|
recoveryService := services.NewRecoveryService(services.Deps{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Repository: userRepo,
|
CodewordRepository: codewordRepo,
|
||||||
Email: recoveryEmailSender,
|
UserRepository: userRepo,
|
||||||
EncryptService: encryptService,
|
EncryptService: encryptService,
|
||||||
})
|
})
|
||||||
|
|
||||||
recoveryController := controller.NewRecoveryController(logger, recoveryService)
|
recoveryController := controller.NewRecoveryController(logger, recoveryService)
|
||||||
|
|
||||||
server := httpserver.NewServer(httpserver.ServerConfig{
|
server := httpserver.NewServer(httpserver.ServerConfig{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Repo: userRepo,
|
|
||||||
RecoveryController: recoveryController,
|
RecoveryController: recoveryController,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -57,8 +57,6 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
fmt.Println("<-ctx.Done()")
|
|
||||||
|
|
||||||
if err := shutdownApp(server, mdb, logger); err != nil {
|
if err := shutdownApp(server, mdb, logger); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"codeword/internal/services"
|
"codeword/internal/services"
|
||||||
"fmt"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
"time"
|
||||||
@ -20,6 +19,10 @@ func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
||||||
|
return r.service.Ping(c.Context())
|
||||||
|
}
|
||||||
|
|
||||||
// HandleRecoveryRequest обрабатывает запрос на восстановление пароля
|
// HandleRecoveryRequest обрабатывает запрос на восстановление пароля
|
||||||
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||||
email := c.FormValue("email")
|
email := c.FormValue("email")
|
||||||
@ -29,24 +32,20 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
|||||||
r.logger.Error("Failed to generate key", zap.Error(err))
|
r.logger.Error("Failed to generate key", zap.Error(err))
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
fmt.Println(key)
|
|
||||||
|
|
||||||
user, err := r.service.FindUserByEmail(email)
|
user, err := r.service.FindUserByEmail(c.Context(), email)
|
||||||
if err != nil {
|
if err != nil || user == nil {
|
||||||
r.logger.Error("Failed to find user by email", zap.Error(err))
|
r.logger.Error("Failed to find user by email", zap.Error(err))
|
||||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||||
}
|
}
|
||||||
fmt.Println(user)
|
|
||||||
// сохраняем в бд
|
err = r.service.StoreRecoveryRecord(c.Context(), user.ID.Hex(), user.Email, key)
|
||||||
signature, err := r.service.StoreRecoveryRecord("user")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to store recovery record", zap.Error(err))
|
r.logger.Error("Failed to store recovery record", zap.Error(err))
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
// тут что-то на подобии канала или что-то подобное так как отправка выполнятеся в воркере,
|
|
||||||
//это пока временное решение для написания структуры кода и проверки отправки, далее перепишу
|
err = r.service.RecoveryEmailTask(c.Context(), user.ID.Hex(), email, key)
|
||||||
// под горутины
|
|
||||||
err = r.service.SendRecoveryEmail(email, signature)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
@ -57,9 +56,9 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// HandleRecoveryLink обрабатывает ссылку восстановления и обменивает ее на токены
|
// HandleRecoveryLink обрабатывает ссылку восстановления и обменивает ее на токены
|
||||||
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
||||||
signature := c.Params("sign")
|
key := c.Params("sign")
|
||||||
// тут получается
|
// тут получается
|
||||||
record, err := r.service.GetRecoveryRecord(signature)
|
record, err := r.service.GetRecoveryRecord(c.Context(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
@ -67,7 +66,7 @@ func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// проверка на более чем 15 минут
|
// проверка на более чем 15 минут
|
||||||
if time.Since(record.CreatedAt) > 15*time.Minute {
|
if time.Since(record.CreatedAt) > 15*time.Minute {
|
||||||
r.logger.Error("Recovery link expired", zap.String("signature", signature))
|
r.logger.Error("Recovery link expired", zap.String("signature", key))
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Recovery link expired"})
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Recovery link expired"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,15 +8,18 @@ type Config struct {
|
|||||||
AppName string `env:"APP_NAME" envDefault:"codeword"`
|
AppName string `env:"APP_NAME" envDefault:"codeword"`
|
||||||
HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"`
|
HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"`
|
||||||
HTTPPort string `env:"HTTP_PORT" envDefault:"3000"`
|
HTTPPort string `env:"HTTP_PORT" envDefault:"3000"`
|
||||||
MongoHost string `env:"MONGO_HOST" envDefault:"localhost"`
|
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
||||||
MongoPort string `env:"MONGO_PORT" envDefault:"27017"`
|
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
||||||
MongoUser string `env:"MONGO_USER" envDefault:"admin"`
|
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
||||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"admin"`
|
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
||||||
MongoDatabase string `env:"MONGO_DB" envDefault:"codeword_db"`
|
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
||||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
||||||
PublicCurveKey string `env:"PUBLIC_CURVE_KEY" envDefault:"test"`
|
PublicCurveKey string `env:"PUBLIC_CURVE_KEY" envDefault:"localhost:6379"`
|
||||||
PrivateCurveKey string `env:"PRIVATE_CURVE_KEY" envDefault:"test"`
|
PrivateCurveKey string `env:"PRIVATE_CURVE_KEY" envDefault:"localhost:6379"`
|
||||||
SignSecret string `env:"SIGN_SECRET" envDefault:"test"`
|
SignSecret string `env:"SIGN_SECRET" envDefault:"localhost:6379"`
|
||||||
|
RedisAddr string `env:"REDIS_ADDR" envDefault:"localhost:6379"`
|
||||||
|
RedisPassword string `env:"REDIS_PASS" envDefault:"admin"`
|
||||||
|
RedisDB int `env:"REDIS_DB" envDefault:"2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
|
21
internal/initialize/redis.go
Normal file
21
internal/initialize/redis.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitializeRedis(ctx context.Context, cfg Config) (*redis.Client, error) {
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: cfg.RedisAddr,
|
||||||
|
Password: cfg.RedisPassword,
|
||||||
|
DB: cfg.RedisDB,
|
||||||
|
})
|
||||||
|
|
||||||
|
err := rdb.Ping(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return rdb, nil
|
||||||
|
}
|
@ -1,9 +1,20 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
// получаем данные из другого сервиса
|
ID primitive.ObjectID `bson:"_id,omitempty"`
|
||||||
|
Login string `bson:"login,omitempty"`
|
||||||
|
Email string `bson:"email,omitempty"`
|
||||||
|
Password string `bson:"password,omitempty"`
|
||||||
|
PhoneNumber string `bson:"phoneNumber,omitempty"`
|
||||||
|
IsDeleted bool `bson:"isDeleted,omitempty"`
|
||||||
|
CreatedAt time.Time `bson:"createdAt,omitempty"`
|
||||||
|
UpdatedAt time.Time `bson:"updatedAt,omitempty"`
|
||||||
|
DeletedAt *time.Time `bson:"deletedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RestoreRequest struct {
|
type RestoreRequest struct {
|
||||||
@ -15,3 +26,10 @@ type RestoreRequest struct {
|
|||||||
Sent bool
|
Sent bool
|
||||||
SentAt time.Time
|
SentAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecoveryRecord struct {
|
||||||
|
UserID string `bson:"user_id"`
|
||||||
|
Email string `bson:"email"`
|
||||||
|
Key string `bson:"key"`
|
||||||
|
CreatedAt time.Time `bson:"created_at"`
|
||||||
|
}
|
||||||
|
@ -3,36 +3,94 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/readpref"
|
"go.mongodb.org/mongo-driver/mongo/readpref"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserRepository struct {
|
type Deps struct {
|
||||||
db *mongo.Database
|
Mdb *mongo.Collection
|
||||||
|
Rdb *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo реализовать
|
type codewordRepository struct {
|
||||||
|
mdb *mongo.Collection
|
||||||
func NewUserRepository(db *mongo.Database) *UserRepository {
|
rdb *redis.Client
|
||||||
return &UserRepository{db}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *UserRepository) FindByEmail(email string) (*models.User, error) {
|
type userRepository struct {
|
||||||
//todo
|
mdb *mongo.Collection
|
||||||
return &models.User{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *UserRepository) StoreRecoveryRecord(userID, signature string, createdAt time.Time) error {
|
func NewUserRepository(deps Deps) *userRepository {
|
||||||
//todo
|
|
||||||
|
return &userRepository{mdb: deps.Mdb}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCodewordRepository(deps Deps) *codewordRepository {
|
||||||
|
|
||||||
|
return &codewordRepository{mdb: deps.Mdb, rdb: deps.Rdb}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) FindByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
|
var user models.User
|
||||||
|
|
||||||
|
err := r.mdb.FindOne(ctx, bson.M{"email": email}).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *codewordRepository) StoreRecoveryRecord(ctx context.Context, userID string, email string, key []byte) error {
|
||||||
|
record := models.RecoveryRecord{
|
||||||
|
UserID: userID,
|
||||||
|
Email: email,
|
||||||
|
Key: string(key),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := r.mdb.InsertOne(ctx, record)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *UserRepository) GetRecoveryRecord(signature string) (*models.RestoreRequest, error) {
|
func (r *codewordRepository) InsertToQueue(ctx context.Context, userID string, email string, key []byte) error {
|
||||||
return &models.RestoreRequest{UserID: "123", Sign: signature, CreatedAt: time.Now()}, nil
|
task := models.RecoveryRecord{
|
||||||
|
UserID: userID,
|
||||||
|
Email: email,
|
||||||
|
Key: string(key),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
taskBytes, err := json.Marshal(task)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqKey := fmt.Sprintf("needRecovery:%d", time.Now().UnixNano())
|
||||||
|
|
||||||
|
if err := r.rdb.Set(ctx, uniqKey, taskBytes, 0).Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *UserRepository) Ping(ctx context.Context) error {
|
func (r *codewordRepository) GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error) {
|
||||||
return r.db.Client().Ping(ctx, readpref.Primary())
|
return &models.RestoreRequest{UserID: "123", Sign: key, CreatedAt: time.Now()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *codewordRepository) Ping(ctx context.Context) error {
|
||||||
|
return r.mdb.Database().Client().Ping(ctx, readpref.Primary())
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
controller "codeword/internal/controller/recovery"
|
controller "codeword/internal/controller/recovery"
|
||||||
"codeword/internal/repository"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@ -12,13 +11,11 @@ import (
|
|||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
Repo *repository.UserRepository
|
|
||||||
RecoveryController *controller.RecoveryController
|
RecoveryController *controller.RecoveryController
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
Repo *repository.UserRepository
|
|
||||||
RecoveryController *controller.RecoveryController
|
RecoveryController *controller.RecoveryController
|
||||||
app *fiber.App
|
app *fiber.App
|
||||||
}
|
}
|
||||||
@ -28,7 +25,6 @@ func NewServer(config ServerConfig) *Server {
|
|||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Logger: config.Logger,
|
Logger: config.Logger,
|
||||||
Repo: config.Repo,
|
|
||||||
RecoveryController: config.RecoveryController,
|
RecoveryController: config.RecoveryController,
|
||||||
app: app,
|
app: app,
|
||||||
}
|
}
|
||||||
@ -61,7 +57,7 @@ func (s *Server) handleLiveness(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
func (s *Server) handleReadiness(c *fiber.Ctx) error {
|
func (s *Server) handleReadiness(c *fiber.Ctx) error {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
if err := s.Repo.Ping(c.Context()); err != nil {
|
if err := s.RecoveryController.HandlePingDB(c); err != nil {
|
||||||
s.Logger.Error("Failed to ping the database", zap.Error(err))
|
s.Logger.Error("Failed to ping the database", zap.Error(err))
|
||||||
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
||||||
}
|
}
|
||||||
|
@ -3,70 +3,75 @@ package services
|
|||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
"codeword/internal/utils/encrypt"
|
"codeword/internal/utils/encrypt"
|
||||||
|
"context"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserRepository interface {
|
type CodewordRepository interface {
|
||||||
FindByEmail(email string) (*models.User, error)
|
StoreRecoveryRecord(ctx context.Context, userID string, email string, key []byte) error
|
||||||
StoreRecoveryRecord(userID string, signature string, createdAt time.Time) error
|
InsertToQueue(ctx context.Context, userID string, email string, key []byte) error
|
||||||
GetRecoveryRecord(signature string) (*models.RestoreRequest, error)
|
Ping(ctx context.Context) error
|
||||||
|
GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmailSender interface {
|
type UserRepository interface {
|
||||||
SendRecoveryEmail(email, signature string) error
|
FindByEmail(ctx context.Context, email string) (*models.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
Repository UserRepository
|
CodewordRepository CodewordRepository
|
||||||
Email EmailSender
|
UserRepository UserRepository
|
||||||
EncryptService *encrypt.Encrypt
|
EncryptService *encrypt.Encrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecoveryService struct {
|
type RecoveryService struct {
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
repository UserRepository
|
repositoryCodeword CodewordRepository
|
||||||
email EmailSender
|
repositoryUser UserRepository
|
||||||
encryptService *encrypt.Encrypt
|
encryptService *encrypt.Encrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecoveryService(deps Deps) *RecoveryService {
|
func NewRecoveryService(deps Deps) *RecoveryService {
|
||||||
return &RecoveryService{
|
return &RecoveryService{
|
||||||
logger: deps.Logger,
|
logger: deps.Logger,
|
||||||
repository: deps.Repository,
|
repositoryCodeword: deps.CodewordRepository,
|
||||||
email: deps.Email,
|
repositoryUser: deps.UserRepository,
|
||||||
encryptService: deps.EncryptService,
|
encryptService: deps.EncryptService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateKey генерирует ключ, используя шифрование на основе эллиптической кривой
|
// GenerateKey генерирует ключ, используя шифрование на основе эллиптической кривой
|
||||||
func (s *RecoveryService) GenerateKey() (string, error) {
|
func (s *RecoveryService) GenerateKey() ([]byte, error) {
|
||||||
// TODO
|
key, err := s.encryptService.SignCommonSecret()
|
||||||
return "", nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RecoveryService) Ping(ctx context.Context) error {
|
||||||
|
return s.repositoryCodeword.Ping(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindUserByEmail ищет пользователя по электронной почте
|
// FindUserByEmail ищет пользователя по электронной почте
|
||||||
func (s *RecoveryService) FindUserByEmail(email string) (*models.User, error) {
|
func (s *RecoveryService) FindUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
return s.repository.FindByEmail(email)
|
return s.repositoryUser.FindByEmail(ctx, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreRecoveryRecord сохраняет запись восстановления в базе данных
|
// StoreRecoveryRecord сохраняет запись восстановления в базе данных
|
||||||
func (s *RecoveryService) StoreRecoveryRecord(userID string) (string, error) {
|
func (s *RecoveryService) StoreRecoveryRecord(ctx context.Context, userID string, email string, key []byte) error {
|
||||||
signature := ""
|
return s.repositoryCodeword.StoreRecoveryRecord(ctx, userID, email, key)
|
||||||
createdAt := time.Now()
|
|
||||||
err := s.repository.StoreRecoveryRecord(userID, signature, createdAt)
|
|
||||||
return signature, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRecoveryRecord получает запись восстановления из базы данных
|
|
||||||
func (s *RecoveryService) GetRecoveryRecord(signature string) (*models.RestoreRequest, error) {
|
|
||||||
return s.repository.GetRecoveryRecord(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRecoveryEmail посылает письмо для восстановления доступа пользователю
|
// SendRecoveryEmail посылает письмо для восстановления доступа пользователю
|
||||||
func (s *RecoveryService) SendRecoveryEmail(email string, signature string) error {
|
func (s *RecoveryService) RecoveryEmailTask(ctx context.Context, userID string, email string, key []byte) error {
|
||||||
return s.email.SendRecoveryEmail(email, signature)
|
return s.repositoryCodeword.InsertToQueue(ctx, userID, email, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecoveryRecord получает запись восстановления из базы данных
|
||||||
|
func (s *RecoveryService) GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error) {
|
||||||
|
return s.repositoryCodeword.GetRecoveryRecord(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExchangeForTokens обменивает ссылку восстановления на токены используя сервис аутентификации.
|
// ExchangeForTokens обменивает ссылку восстановления на токены используя сервис аутентификации.
|
||||||
|
@ -7,11 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
MongoHost string `env:"MONGO_HOST" envDefault:"localhost"`
|
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
||||||
MongoPort string `env:"MONGO_PORT" envDefault:"27017"`
|
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
||||||
MongoUser string `env:"MONGO_USER" envDefault:"admin"`
|
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
||||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"admin"`
|
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
||||||
MongoDatabase string `env:"MONGO_DB" envDefault:"codeword_db"`
|
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
||||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ func Connect(ctx context.Context, deps *ConnectDeps) (*mongo.Database, error) {
|
|||||||
connectionOptions := options.Client().
|
connectionOptions := options.Client().
|
||||||
ApplyURI(mongoURI.String()).
|
ApplyURI(mongoURI.String()).
|
||||||
SetAuth(options.Credential{
|
SetAuth(options.Credential{
|
||||||
AuthMechanism: "SCRAM-SHA-1",
|
AuthMechanism: "SCRAM-SHA-256",
|
||||||
AuthSource: deps.Configuration.MongoAuth,
|
AuthSource: deps.Configuration.MongoAuth,
|
||||||
Username: deps.Configuration.MongoUser,
|
Username: deps.Configuration.MongoUser,
|
||||||
Password: deps.Configuration.MongoPassword,
|
Password: deps.Configuration.MongoPassword,
|
||||||
|
91
tests/repository_test/repository_test.go
Normal file
91
tests/repository_test/repository_test.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package repository_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/repository"
|
||||||
|
"context"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pioz/faker"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const mongoURI = "mongodb://test:test@127.0.0.1:27020/?authMechanism=SCRAM-SHA-256&authSource=admin&directConnection=true"
|
||||||
|
|
||||||
|
func TestFindByEmail(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
client, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to connect to MongoDB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := client.Disconnect(ctx); err != nil {
|
||||||
|
log.Fatalf("Failed to disconnect MongoDB client: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := client.Ping(ctx, nil); err != nil {
|
||||||
|
log.Fatalf("Failed to ping MongoDB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := client.Database("admin")
|
||||||
|
|
||||||
|
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: db.Collection("users")})
|
||||||
|
|
||||||
|
t.Run("FindByEmail - existing user", func(t *testing.T) {
|
||||||
|
user, err := userRepo.FindByEmail(ctx, "email@mail.ru")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
assert.Equal(t, "email@mail.ru", user.Email)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FindByEmail - non-existing user", func(t *testing.T) {
|
||||||
|
user, err := userRepo.FindByEmail(ctx, "nonexisting@example.com")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, user)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreRecoveryRecord(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
codeword := database.Collection("codeword")
|
||||||
|
_ = codeword.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewCodewordRepository(repository.Deps{Rdb: nil, Mdb: codeword})
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
userID := faker.String()
|
||||||
|
email := faker.Email()
|
||||||
|
key := []byte("test_recovery_key")
|
||||||
|
|
||||||
|
err = userRepo.StoreRecoveryRecord(ctx, userID, email, key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var storedRecord models.RecoveryRecord
|
||||||
|
err = codeword.FindOne(ctx, bson.M{"user_id": userID}).Decode(&storedRecord)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, email, storedRecord.Email)
|
||||||
|
assert.Equal(t, string(key), storedRecord.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = database.Drop(ctx)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user