generated from PenaSide/GolangTemplate
feat: account crud
This commit is contained in:
parent
8bfd447d46
commit
7060ec31f7
@ -2,42 +2,35 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
formatter "github.com/antonfisher/nested-logrus-formatter"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/app"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
logger := logrus.New()
|
||||
logger, err := zap.NewProduction(zap.AddCaller())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to init zap logger: %v", err)
|
||||
}
|
||||
|
||||
defer cancel()
|
||||
|
||||
logger.SetReportCaller(true)
|
||||
logger.SetFormatter(&formatter.Formatter{
|
||||
TimestampFormat: "02-01-2006 15:04:05",
|
||||
HideKeys: true,
|
||||
NoColors: false,
|
||||
NoFieldsSpace: true,
|
||||
CustomCallerFormatter: func(frame *runtime.Frame) string {
|
||||
return fmt.Sprintf(" (%s:%s)", path.Base(frame.File), strconv.Itoa(frame.Line))
|
||||
},
|
||||
})
|
||||
defer logger.Sync()
|
||||
|
||||
config, err := initialize.Configuration(".env.test")
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to init config: %v", err)
|
||||
logger.Fatal("failed to init config: %v",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
|
||||
if err := app.Run(ctx, config, logger); err != nil {
|
||||
logger.Fatalf("failed to run app: %v", err)
|
||||
logger.Fatal("failed to run app: %v",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
12
go.mod
12
go.mod
@ -20,6 +20,7 @@ require (
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/brpaz/echozap v1.1.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
@ -36,7 +37,7 @@ require (
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.4 // indirect
|
||||
@ -48,10 +49,13 @@ require (
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
34
go.sum
34
go.sum
@ -6,12 +6,15 @@ github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBa
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/brpaz/echozap v1.1.3 h1:6cmi4m8/XwUckFH+cfsvX9eRomVOOs01AWDakEcDRCk=
|
||||
github.com/brpaz/echozap v1.1.3/go.mod h1:5NJmhB1VsJbB8cyks5qft57uvgJwgls3t5tJbThIM4Y=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
|
||||
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E=
|
||||
github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
@ -60,8 +63,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -69,13 +74,18 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
@ -84,6 +94,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
|
||||
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -99,6 +110,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -114,6 +126,7 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
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/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
@ -127,21 +140,39 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
|
||||
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -150,8 +181,11 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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=
|
||||
|
@ -5,10 +5,12 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/server"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/closer"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
|
||||
)
|
||||
@ -17,7 +19,7 @@ const (
|
||||
shutdownTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
func Run(ctx context.Context, config *models.Config, logger *logrus.Logger) error {
|
||||
func Run(ctx context.Context, config *models.Config, logger *zap.Logger) error {
|
||||
mongoDB, err := mongo.Connect(ctx, &mongo.ConnectDeps{
|
||||
Configuration: &config.Database,
|
||||
Timeout: 10 * time.Second,
|
||||
@ -27,11 +29,8 @@ func Run(ctx context.Context, config *models.Config, logger *logrus.Logger) erro
|
||||
}
|
||||
|
||||
clients := initialize.NewClients(&initialize.ClientsDeps{
|
||||
Logger: logger,
|
||||
GoogleURL: &config.Service.Google.URL,
|
||||
AmocrmURL: &config.Service.Amocrm.URL,
|
||||
AuthURL: &config.Service.AuthMicroservice.URL,
|
||||
AmocrmOAuthConfiguration: &config.Service.Amocrm.OAuthConfig,
|
||||
Logger: logger,
|
||||
AuthURL: &config.Service.AuthMicroservice.URL,
|
||||
})
|
||||
|
||||
repositories := initialize.NewRepositories(&initialize.RepositoriesDeps{
|
||||
@ -51,8 +50,21 @@ func Run(ctx context.Context, config *models.Config, logger *logrus.Logger) erro
|
||||
Services: services,
|
||||
})
|
||||
|
||||
httpServer := server.New(logger).Register(controllers)
|
||||
closer := closer.New(logger)
|
||||
openAPI, err := swagger.GetSwagger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to loading openapi spec: %w", err)
|
||||
}
|
||||
|
||||
swagger := initialize.NewSwagger(controllers)
|
||||
closer := closer.New()
|
||||
|
||||
httpServer := server.New(&server.Deps{
|
||||
Logger: logger,
|
||||
Swagger: openAPI,
|
||||
JWTUtil: utils.NewJWT[models.AuthJWTDecoded](&config.Service.JWT),
|
||||
})
|
||||
|
||||
httpServer.Register(swagger)
|
||||
|
||||
go httpServer.Run(&config.HTTP)
|
||||
|
||||
@ -61,7 +73,7 @@ func Run(ctx context.Context, config *models.Config, logger *logrus.Logger) erro
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
logger.Infoln("shutting down app gracefully")
|
||||
logger.Info("shutting down app gracefully")
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
|
||||
|
@ -2,9 +2,10 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
|
||||
@ -12,12 +13,12 @@ import (
|
||||
)
|
||||
|
||||
type AuthClientDeps struct {
|
||||
Logger *logrus.Logger
|
||||
Logger *zap.Logger
|
||||
URLs *models.AuthMicroServiceURL
|
||||
}
|
||||
|
||||
type AuthClient struct {
|
||||
logger *logrus.Logger
|
||||
logger *zap.Logger
|
||||
urls *models.AuthMicroServiceURL
|
||||
}
|
||||
|
||||
@ -28,23 +29,40 @@ func NewAuthClient(deps *AuthClientDeps) *AuthClient {
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *AuthClient) GetUser(ctx context.Context, userID string) (*models.AuthUser, error) {
|
||||
func (receiver *AuthClient) GetUser(ctx context.Context, userID string) (*models.User, errors.Error) {
|
||||
userURL, err := url.JoinPath(receiver.urls.User, userID)
|
||||
if err != nil {
|
||||
return nil, errors.ErrInvalidReturnValue
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("failed to join path on <GetUser> of <AuthClient>: %w", err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
}
|
||||
|
||||
response, err := client.Get[models.AuthUser, models.FastifyError](ctx, &client.RequestSettings{
|
||||
response, err := client.Get[models.User, models.FastifyError](ctx, &client.RequestSettings{
|
||||
URL: userURL,
|
||||
Headers: map[string]string{"Content-Type": "application/json"},
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Errorf("failed to get user on <GetUser> of <AuthClient>: %v", err)
|
||||
return nil, err
|
||||
receiver.logger.Error("failed to request get user on <GetUser> of <AuthClient>",
|
||||
zap.Error(err),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("failed to request get user with <%s> on <GetUser> of <AuthClient>: %w", userID, err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
}
|
||||
if response.Error != nil {
|
||||
receiver.logger.Errorf("failed request on <GetUser> of <AuthClient>: %s", response.Error.Message)
|
||||
return nil, utils.DetermineClientErrorResponse(response.StatusCode)
|
||||
receiver.logger.Error("failed request on <GetUser> of <AuthClient>",
|
||||
zap.String("error", response.Error.Message),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("failed request with <%s> on <GetUser> of <AuthClient>: %w", userID, err),
|
||||
utils.DetermineClientErrorResponse(response.StatusCode),
|
||||
)
|
||||
}
|
||||
|
||||
return response.Body, nil
|
||||
|
121
internal/controller/account/account.go
Normal file
121
internal/controller/account/account.go
Normal file
@ -0,0 +1,121 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
|
||||
)
|
||||
|
||||
type accountService interface {
|
||||
GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error)
|
||||
GetAccountsList(ctx context.Context, pagination *models.Pagination) ([]models.Account, errors.Error)
|
||||
CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error)
|
||||
CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error)
|
||||
RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error)
|
||||
DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error)
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Service accountService
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
logger *zap.Logger
|
||||
service accountService
|
||||
}
|
||||
|
||||
func New(deps *Deps) *Controller {
|
||||
return &Controller{
|
||||
logger: deps.Logger,
|
||||
service: deps.Service,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *Controller) GetAccount(ctx echo.Context) error {
|
||||
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
|
||||
if !ok {
|
||||
return utils.DetermineEchoErrorResponse(ctx, errors.New(
|
||||
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
|
||||
errors.ErrInvalidArgs,
|
||||
))
|
||||
}
|
||||
|
||||
account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, account)
|
||||
}
|
||||
|
||||
func (receiver *Controller) GetDirectAccount(ctx echo.Context, userId string) error {
|
||||
account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userId)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, account)
|
||||
}
|
||||
|
||||
func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.PaginationAccountsParams) error {
|
||||
accounts, err := receiver.service.GetAccountsList(
|
||||
ctx.Request().Context(),
|
||||
utils.DeterminePagination(params.Page, params.Limit),
|
||||
)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, accounts)
|
||||
}
|
||||
|
||||
func (receiver *Controller) CreateAccount(ctx echo.Context) error {
|
||||
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
|
||||
if !ok {
|
||||
return utils.DetermineEchoErrorResponse(ctx, errors.New(
|
||||
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
|
||||
errors.ErrInvalidArgs,
|
||||
))
|
||||
}
|
||||
|
||||
account, err := receiver.service.CreateAccountByUserID(ctx.Request().Context(), userID)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, account)
|
||||
}
|
||||
|
||||
func (receiver *Controller) RemoveAccount(ctx echo.Context) error {
|
||||
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
|
||||
if !ok {
|
||||
return utils.DetermineEchoErrorResponse(ctx, errors.New(
|
||||
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
|
||||
errors.ErrInvalidArgs,
|
||||
))
|
||||
}
|
||||
|
||||
account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, account)
|
||||
}
|
||||
|
||||
func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userId string) error {
|
||||
account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userId)
|
||||
if err != nil {
|
||||
return utils.DetermineEchoErrorResponse(ctx, err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, account)
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNoServerItem = errors.New("microservice/service has no such item")
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidReturnValue = errors.New("method of function returned invalid value")
|
||||
ErrEmptyArgs = errors.New("empty arguments or nil argument")
|
||||
ErrInvalidArgs = errors.New("invalid arguments")
|
||||
ErrMethodNotImplemented = errors.New("method is not implemented")
|
||||
)
|
56
internal/errors/errors.go
Normal file
56
internal/errors/errors.go
Normal file
@ -0,0 +1,56 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ErrorType error
|
||||
|
||||
var (
|
||||
ErrInternalError ErrorType = errors.New("internal error")
|
||||
ErrInvalidArgs ErrorType = errors.New("invalid arguments")
|
||||
ErrMethodNotImplemented ErrorType = errors.New("method is not implemented")
|
||||
ErrNotFound ErrorType = errors.New("record not found")
|
||||
ErrNoAccess ErrorType = errors.New("no access")
|
||||
)
|
||||
|
||||
type Error interface {
|
||||
Error() string
|
||||
Extract() error
|
||||
Type() ErrorType
|
||||
Wrap(err error)
|
||||
SetType(errorType ErrorType)
|
||||
}
|
||||
|
||||
type customError struct {
|
||||
errorType ErrorType
|
||||
err error
|
||||
}
|
||||
|
||||
func New(err error, errorType ErrorType) *customError {
|
||||
return &customError{
|
||||
errorType: errorType,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *customError) Error() string {
|
||||
return receiver.err.Error()
|
||||
}
|
||||
|
||||
func (receiver *customError) Type() ErrorType {
|
||||
return receiver.errorType
|
||||
}
|
||||
|
||||
func (receiver *customError) Wrap(err error) {
|
||||
receiver.err = fmt.Errorf("%w: %w", receiver.err, err)
|
||||
}
|
||||
|
||||
func (receiver *customError) SetType(errorType ErrorType) {
|
||||
receiver.errorType = errorType
|
||||
}
|
||||
|
||||
func (receiver *customError) Extract() error {
|
||||
return receiver.err
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNoRecord = errors.New("no record in db")
|
||||
ErrInsertRecord = errors.New("failed to insert record")
|
||||
ErrReadRecord = errors.New("failed to read record")
|
||||
ErrFindRecord = errors.New("failed to find record")
|
||||
ErrDecodeRecord = errors.New("failed to decode structure")
|
||||
ErrTransaction = errors.New("failed transaction")
|
||||
ErrTransactionSessionStart = errors.New("failed to start transaction session")
|
||||
ErrUpdateRecord = errors.New("failed to update record")
|
||||
ErrRecordAlreadyExist = errors.New("record already exist")
|
||||
)
|
21
internal/fields/account.go
Normal file
21
internal/fields/account.go
Normal file
@ -0,0 +1,21 @@
|
||||
package fields
|
||||
|
||||
var Account = struct {
|
||||
ID string
|
||||
UserID string
|
||||
Cart string
|
||||
Wallet string
|
||||
Deleted string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}{
|
||||
ID: "_id",
|
||||
UserID: "userId",
|
||||
Cart: "cart",
|
||||
Wallet: "wallet",
|
||||
Deleted: "deleted",
|
||||
CreatedAt: "createdAt",
|
||||
UpdatedAt: "updatedAt",
|
||||
DeletedAt: "deletedAt",
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package fields
|
||||
|
||||
var AmocrmUser = struct {
|
||||
ID string
|
||||
AmocrmID string
|
||||
UserID string
|
||||
Information string
|
||||
Audit string
|
||||
}{
|
||||
ID: "id",
|
||||
AmocrmID: "amocrmId",
|
||||
UserID: "userId",
|
||||
Information: "information",
|
||||
Audit: auditFieldName,
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package fields
|
||||
|
||||
import "fmt"
|
||||
|
||||
var auditFieldName = "audit"
|
||||
|
||||
var Audit = struct {
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
CreatedAt string
|
||||
Deleted string
|
||||
}{
|
||||
UpdatedAt: fmt.Sprintf("%s.updatedAt", auditFieldName),
|
||||
DeletedAt: fmt.Sprintf("%s.deletedAt", auditFieldName),
|
||||
CreatedAt: fmt.Sprintf("%s.createdAt", auditFieldName),
|
||||
Deleted: fmt.Sprintf("%s.deleted", auditFieldName),
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package fields
|
||||
|
||||
import "fmt"
|
||||
|
||||
// TODO: актуализировать поля для google user'а
|
||||
|
||||
var GoogleFields = struct {
|
||||
Subject string
|
||||
Fullname string
|
||||
GivenName string
|
||||
FamilyName string
|
||||
PictureURL string
|
||||
Email string
|
||||
EmailVerified string
|
||||
Locale string
|
||||
}{
|
||||
Subject: fmt.Sprintf("%s.sub", UserFields.Google),
|
||||
Fullname: fmt.Sprintf("%s.name", UserFields.Google),
|
||||
GivenName: fmt.Sprintf("%s.given_name", UserFields.Google),
|
||||
FamilyName: fmt.Sprintf("%s.family_name", UserFields.Google),
|
||||
PictureURL: fmt.Sprintf("%s.picture", UserFields.Google),
|
||||
Email: fmt.Sprintf("%s.email", UserFields.Google),
|
||||
EmailVerified: fmt.Sprintf("%s.email_verified", UserFields.Google),
|
||||
Locale: fmt.Sprintf("%s.locale", UserFields.Google),
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package fields
|
||||
|
||||
var UserFields = struct {
|
||||
ID string
|
||||
UserID string
|
||||
GoogleID string
|
||||
VKID string
|
||||
Google string
|
||||
VK string
|
||||
Amocrm string
|
||||
}{
|
||||
ID: "_id",
|
||||
UserID: "userId",
|
||||
GoogleID: "googleId",
|
||||
VKID: "vkId",
|
||||
Google: "googleInformation",
|
||||
VK: "vkInformation",
|
||||
Amocrm: "amocrmInformation",
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package fields
|
||||
|
||||
import "fmt"
|
||||
|
||||
// TODO: актуализировать поля для vk user'а
|
||||
|
||||
var VKFields = struct {
|
||||
ID string
|
||||
FirstName string
|
||||
LastName string
|
||||
Photo string
|
||||
Sex string
|
||||
Domain string
|
||||
ScreenName string
|
||||
Birthday string
|
||||
PhotoID string
|
||||
FollowersCount string
|
||||
HomeTown string
|
||||
Timezone string
|
||||
MobilePhone string
|
||||
Email string
|
||||
}{
|
||||
ID: fmt.Sprintf("%s.id", UserFields.VK),
|
||||
FirstName: fmt.Sprintf("%s.firstname", UserFields.VK),
|
||||
LastName: fmt.Sprintf("%s.lastname", UserFields.VK),
|
||||
Photo: fmt.Sprintf("%s.avatar", UserFields.VK),
|
||||
Sex: fmt.Sprintf("%s.sex", UserFields.VK),
|
||||
Domain: fmt.Sprintf("%s.domain", UserFields.VK),
|
||||
ScreenName: fmt.Sprintf("%s.screen_name", UserFields.VK),
|
||||
Birthday: fmt.Sprintf("%s.birthday", UserFields.VK),
|
||||
PhotoID: fmt.Sprintf("%s.photo_id", UserFields.VK),
|
||||
FollowersCount: fmt.Sprintf("%s.followers_count", UserFields.VK),
|
||||
HomeTown: fmt.Sprintf("%s.home_town", UserFields.VK),
|
||||
Timezone: fmt.Sprintf("%s.timezone", UserFields.VK),
|
||||
MobilePhone: fmt.Sprintf("%s.mobile_phone", UserFields.VK),
|
||||
Email: fmt.Sprintf("%s.email", UserFields.VK),
|
||||
}
|
@ -1,18 +1,14 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/client"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
)
|
||||
|
||||
type ClientsDeps struct {
|
||||
Logger *logrus.Logger
|
||||
GoogleURL *models.GoogleURL
|
||||
AmocrmURL *models.AmocrmURL
|
||||
AuthURL *models.AuthMicroServiceURL
|
||||
AmocrmOAuthConfiguration *oauth2.Config
|
||||
Logger *zap.Logger
|
||||
AuthURL *models.AuthMicroServiceURL
|
||||
}
|
||||
|
||||
type Clients struct {
|
||||
|
@ -4,9 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/vk"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/env"
|
||||
@ -22,46 +19,11 @@ func Configuration(path string) (*models.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initOAuth2Configuration(&config.Service)
|
||||
iniJWTConfiguration(&config.Service.JWT)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func initOAuth2Configuration(config *models.ServiceConfiguration) {
|
||||
config.Google.OAuthConfig = oauth2.Config{
|
||||
RedirectURL: config.Google.URL.Redirect,
|
||||
ClientID: config.Google.ClientID,
|
||||
ClientSecret: config.Google.ClientSecret,
|
||||
Scopes: []string{
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
},
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
|
||||
config.VK.OAuthConfig = oauth2.Config{
|
||||
RedirectURL: config.VK.URL.Redirect,
|
||||
ClientID: config.VK.ClientID,
|
||||
ClientSecret: config.VK.ClientSecret,
|
||||
Scopes: []string{"email"},
|
||||
Endpoint: vk.Endpoint,
|
||||
}
|
||||
|
||||
config.Amocrm.OAuthConfig = oauth2.Config{
|
||||
RedirectURL: config.Amocrm.URL.Redirect,
|
||||
ClientID: config.Amocrm.ClientID,
|
||||
ClientSecret: config.Amocrm.ClientSecret,
|
||||
Scopes: nil,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: config.Amocrm.URL.OAuthHost,
|
||||
TokenURL: config.Amocrm.URL.AccessToken,
|
||||
AuthStyle: models.BodyAuthStyle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func iniJWTConfiguration(config *models.JWTConfiguration) {
|
||||
config.Algorithm = *jwt.SigningMethodRS256
|
||||
config.ExpiresIn = 15 * time.Minute
|
||||
|
@ -6,9 +6,6 @@ import (
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/vk"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
|
||||
@ -17,58 +14,8 @@ import (
|
||||
func setDefaultTestingENV(t *testing.T) *models.Config {
|
||||
t.Helper()
|
||||
|
||||
defaultGoogleURL := models.GoogleURL{
|
||||
Redirect: "http://www.google.com/callback",
|
||||
OAuthHost: "http://www.google.com/oauth",
|
||||
}
|
||||
|
||||
defaultVKURL := models.VKURL{
|
||||
Redirect: "http://www.vk.ru/callback",
|
||||
}
|
||||
|
||||
defaultAmocrmURL := models.AmocrmURL{
|
||||
Redirect: "http://www.amocrm.ru/callback",
|
||||
OAuthHost: "http://www.amocrm.ru/oauth",
|
||||
UserInfo: "http://www.amocrm.ru/user",
|
||||
AccessToken: "http://www.amocrm.ru/token",
|
||||
}
|
||||
|
||||
defaultAuthURL := models.AuthMicroServiceURL{
|
||||
Exchange: "http://www.auth.ru/callback",
|
||||
Register: "http://www.auth.ru/register",
|
||||
User: "http://www.auth.ru/user",
|
||||
}
|
||||
|
||||
defaultGoogleOAuthConfiguration := oauth2.Config{
|
||||
RedirectURL: defaultGoogleURL.Redirect,
|
||||
ClientID: "google_client_id",
|
||||
ClientSecret: "google_client_secret",
|
||||
Scopes: []string{
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
},
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
|
||||
defaultVKOAuthConfiguration := oauth2.Config{
|
||||
RedirectURL: defaultVKURL.Redirect,
|
||||
ClientID: "vk_client_id",
|
||||
ClientSecret: "vk_client_secret",
|
||||
Scopes: []string{"email"},
|
||||
Endpoint: vk.Endpoint,
|
||||
}
|
||||
|
||||
defaultAmocrmOAuthConfiguration := oauth2.Config{
|
||||
RedirectURL: defaultAmocrmURL.Redirect,
|
||||
ClientID: "amocrm_client_id",
|
||||
ClientSecret: "amocrm_client_secret",
|
||||
Scopes: nil,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: defaultAmocrmURL.OAuthHost,
|
||||
TokenURL: defaultAmocrmURL.AccessToken,
|
||||
AuthStyle: models.BodyAuthStyle,
|
||||
},
|
||||
User: "http://www.auth.ru/user",
|
||||
}
|
||||
|
||||
defaultConfiguration := models.Config{
|
||||
@ -77,28 +24,8 @@ func setDefaultTestingENV(t *testing.T) *models.Config {
|
||||
Port: "8080",
|
||||
},
|
||||
Service: models.ServiceConfiguration{
|
||||
Google: models.GoogleConfiguration{
|
||||
ClientID: defaultGoogleOAuthConfiguration.ClientID,
|
||||
ClientSecret: defaultGoogleOAuthConfiguration.ClientSecret,
|
||||
OAuthConfig: defaultGoogleOAuthConfiguration,
|
||||
URL: defaultGoogleURL,
|
||||
},
|
||||
VK: models.VKConfiguration{
|
||||
ClientID: defaultVKOAuthConfiguration.ClientID,
|
||||
ClientSecret: defaultVKOAuthConfiguration.ClientSecret,
|
||||
OAuthConfig: defaultVKOAuthConfiguration,
|
||||
URL: defaultVKURL,
|
||||
},
|
||||
Amocrm: models.AmocrmConfiguration{
|
||||
ClientID: defaultAmocrmOAuthConfiguration.ClientID,
|
||||
ClientSecret: defaultAmocrmOAuthConfiguration.ClientSecret,
|
||||
OAuthConfig: defaultAmocrmOAuthConfiguration,
|
||||
URL: defaultAmocrmURL,
|
||||
},
|
||||
AuthMicroservice: models.AuthMicroserviceConfiguration{
|
||||
AuthGroup: "group",
|
||||
PrivateSignKey: "key",
|
||||
URL: defaultAuthURL,
|
||||
URL: defaultAuthURL,
|
||||
},
|
||||
JWT: models.JWTConfiguration{
|
||||
PrivateKey: "jwt private key",
|
||||
@ -124,26 +51,6 @@ func setDefaultTestingENV(t *testing.T) *models.Config {
|
||||
t.Setenv("JWT_ISSUER", defaultConfiguration.Service.JWT.Issuer)
|
||||
t.Setenv("JWT_AUDIENCE", defaultConfiguration.Service.JWT.Audience)
|
||||
|
||||
t.Setenv("GOOGLE_CLIENT_ID", defaultConfiguration.Service.Google.ClientID)
|
||||
t.Setenv("GOOGLE_CLIENT_SECRET", defaultConfiguration.Service.Google.ClientSecret)
|
||||
t.Setenv("GOOGLE_REDIRECT_URL", defaultConfiguration.Service.Google.URL.Redirect)
|
||||
t.Setenv("GOOGLE_OAUTH_HOST", defaultConfiguration.Service.Google.URL.OAuthHost)
|
||||
|
||||
t.Setenv("VK_CLIENT_ID", defaultConfiguration.Service.VK.ClientID)
|
||||
t.Setenv("VK_CLIENT_SECRET", defaultConfiguration.Service.VK.ClientSecret)
|
||||
t.Setenv("VK_REDIRECT_URL", defaultConfiguration.Service.VK.URL.Redirect)
|
||||
|
||||
t.Setenv("AMOCRM_CLIENT_ID", defaultConfiguration.Service.Amocrm.ClientID)
|
||||
t.Setenv("AMOCRM_CLIENT_SECRET", defaultConfiguration.Service.Amocrm.ClientSecret)
|
||||
t.Setenv("AMOCRM_REDIRECT_URL", defaultConfiguration.Service.Amocrm.URL.Redirect)
|
||||
t.Setenv("AMOCRM_OAUTH_HOST", defaultConfiguration.Service.Amocrm.URL.OAuthHost)
|
||||
t.Setenv("AMOCRM_USER_INFO_URL", defaultConfiguration.Service.Amocrm.URL.UserInfo)
|
||||
t.Setenv("AMOCRM_ACCESS_TOKEN_URL", defaultConfiguration.Service.Amocrm.URL.AccessToken)
|
||||
|
||||
t.Setenv("AUTH_MICROSERVICE_GROUP", defaultConfiguration.Service.AuthMicroservice.AuthGroup)
|
||||
t.Setenv("AUTH_MICROSERVICE_PRIVATE_SIGN_KEY", defaultConfiguration.Service.AuthMicroservice.PrivateSignKey)
|
||||
t.Setenv("AUTH_MICROSERVICE_EXHANGE_URL", defaultConfiguration.Service.AuthMicroservice.URL.Exchange)
|
||||
t.Setenv("AUTH_MICROSERVICE_REGISTER_URL", defaultConfiguration.Service.AuthMicroservice.URL.Register)
|
||||
t.Setenv("AUTH_MICROSERVICE_USER_URL", defaultConfiguration.Service.AuthMicroservice.URL.User)
|
||||
|
||||
t.Setenv("MONGO_HOST", defaultConfiguration.Database.Host)
|
||||
|
@ -1,17 +1,24 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/controller/account"
|
||||
)
|
||||
|
||||
type ControllersDeps struct {
|
||||
Logger *logrus.Logger
|
||||
Logger *zap.Logger
|
||||
Services *Services
|
||||
}
|
||||
|
||||
type Controllers struct {
|
||||
AccountController *account.Controller
|
||||
}
|
||||
|
||||
func NewControllers(deps *ControllersDeps) *Controllers {
|
||||
return &Controllers{}
|
||||
return &Controllers{
|
||||
AccountController: account.New(&account.Deps{
|
||||
Logger: deps.Logger,
|
||||
Service: deps.Services.AccountService,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,27 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/repository"
|
||||
)
|
||||
|
||||
type RepositoriesDeps struct {
|
||||
MongoDB *mongo.Database
|
||||
Logger *logrus.Logger
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type Repositories struct {
|
||||
HealthRepository *repository.HealthRepository
|
||||
GoogleRepository *repository.GoogleRepository
|
||||
AmocrmRepository *repository.AmocrmRepository
|
||||
HealthRepository *repository.HealthRepository
|
||||
AccountRepository *repository.AccountRepository
|
||||
}
|
||||
|
||||
func NewRepositories(deps *RepositoriesDeps) *Repositories {
|
||||
return &Repositories{
|
||||
HealthRepository: repository.NewHealthRepository(deps.MongoDB),
|
||||
AmocrmRepository: repository.NewAmocrmRepository(
|
||||
deps.MongoDB.Collection("amocrm"),
|
||||
deps.Logger,
|
||||
),
|
||||
GoogleRepository: repository.NewGoogleRepository(
|
||||
deps.MongoDB.Collection("google"),
|
||||
deps.Logger,
|
||||
),
|
||||
AccountRepository: repository.NewAccountRepository(&repository.AccountRepositoryDeps{
|
||||
MongoDB: deps.MongoDB.Collection("accounts"),
|
||||
Logger: deps.Logger,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ func TestNewRepositories(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.NotNil(t, repositories)
|
||||
assert.NotNil(t, repositories.AmocrmRepository)
|
||||
assert.NotNil(t, repositories.GoogleRepository)
|
||||
assert.NotNil(t, repositories.AccountRepository)
|
||||
assert.NotNil(t, repositories.HealthRepository)
|
||||
})
|
||||
})
|
||||
|
@ -1,20 +1,27 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/account"
|
||||
)
|
||||
|
||||
type ServicesDeps struct {
|
||||
Logger *logrus.Logger
|
||||
Logger *zap.Logger
|
||||
Config *models.ServiceConfiguration
|
||||
Repositories *Repositories
|
||||
Clients *Clients
|
||||
}
|
||||
|
||||
type Services struct {
|
||||
AccountService *account.Service
|
||||
}
|
||||
|
||||
func NewServices(deps *ServicesDeps) *Services {
|
||||
return &Services{}
|
||||
return &Services{
|
||||
AccountService: account.New(&account.Deps{
|
||||
Logger: deps.Logger,
|
||||
Repository: deps.Repositories.AccountRepository,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
7
internal/initialize/swagger.go
Normal file
7
internal/initialize/swagger.go
Normal file
@ -0,0 +1,7 @@
|
||||
package initialize
|
||||
|
||||
import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger"
|
||||
|
||||
func NewSwagger(controllers *Controllers) *swagger.API {
|
||||
return swagger.New(&swagger.Deps{})
|
||||
}
|
25
internal/models/account.go
Normal file
25
internal/models/account.go
Normal file
@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Account struct {
|
||||
ID string `json:"id" bson:"_id"`
|
||||
UserID string `json:"userId" bson:"userId"`
|
||||
Cart []string `json:"cart" bson:"cart"`
|
||||
Wallet Wallet `json:"wallet" bson:"wallet"`
|
||||
Deleted bool `json:"deleted" bson:"deleted"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
type Wallet struct {
|
||||
Cash int64 `json:"cash"`
|
||||
Currency string `json:"currency"`
|
||||
|
||||
/*
|
||||
Money деньги на счету в копейках. Чтобы при перессчётах не возникало денег из ни откуда.
|
||||
Фиксируемся к одной валюте, она будет внутренней, никому её не покажем.
|
||||
*/
|
||||
Money int64 `json:"money"`
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package models
|
||||
|
||||
type AmocrmUser struct {
|
||||
ID string `json:"id" bson:"_id,omitempty"`
|
||||
AmocrmID string `json:"amocrmId" bson:"amocrmId"`
|
||||
UserID string `json:"userId,omitempty" bson:"userId,omitempty"`
|
||||
Information AmocrmUserInformation `json:"information" bson:"information"`
|
||||
Audit Audit `json:"audit" bson:"audit"`
|
||||
}
|
||||
|
||||
type AmocrmUserInformation struct {
|
||||
ID int64 `json:"id" bson:"id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Subdomain string `json:"subdomain" bson:"subdomain"`
|
||||
CreatedAt int `json:"created_at" bson:"created_at"`
|
||||
CreatedBy int `json:"created_by" bson:"created_by"`
|
||||
UpdatedAt int `json:"updated_at" bson:"updated_at"`
|
||||
UpdatedBy int `json:"updated_by" bson:"updated_by"`
|
||||
CurrentUserID int `json:"current_user_id" bson:"current_user_id"`
|
||||
Country string `json:"country" bson:"country"`
|
||||
CustomersMode string `json:"customers_mode" bson:"customers_mode"`
|
||||
IsUnsortedOn bool `json:"is_unsorted_on" bson:"is_unsorted_on"`
|
||||
IsLossReasonEnabled bool `json:"is_loss_reason_enabled" bson:"is_loss_reason_enabled"`
|
||||
IsHelpbotEnabled bool `json:"is_helpbot_enabled" bson:"is_helpbot_enabled"`
|
||||
IsTechnicalAccount bool `json:"is_technical_account" bson:"is_technical_account"`
|
||||
ContactNameDisplayOrder int `json:"contact_name_display_order" bson:"contact_name_display_order"`
|
||||
AmojoID string `json:"amojo_id" bson:"amojo_id"`
|
||||
UUID string `json:"uuid" bson:"uuid"`
|
||||
Version int `json:"version" bson:"version"`
|
||||
Links struct {
|
||||
Self struct {
|
||||
Href string `json:"href" bson:"href"`
|
||||
} `json:"self" bson:"self"`
|
||||
} `json:"_links" bson:"_links"`
|
||||
Embedded struct {
|
||||
AmojoRights struct {
|
||||
CanDirect bool `json:"can_direct" bson:"can_direct"`
|
||||
CanCreateGroups bool `json:"can_create_groups" bson:"can_create_groups"`
|
||||
} `json:"amojo_rights" bson:"amojo_rights"`
|
||||
UsersGroups []struct {
|
||||
ID int `json:"id" bson:"id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
UUID interface{} `json:"uuid" bson:"uuid"`
|
||||
} `json:"users_groups" bson:"users_groups"`
|
||||
TaskTypes []struct {
|
||||
ID int `json:"id" bson:"id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Color interface{} `json:"color" bson:"color"`
|
||||
IconID interface{} `json:"icon_id" bson:"icon_id"`
|
||||
Code string `json:"code" bson:"code"`
|
||||
} `json:"task_types" bson:"task_types"`
|
||||
EntityNames struct {
|
||||
Leads struct {
|
||||
Ru struct {
|
||||
Gender string `json:"gender" bson:"gender"`
|
||||
PluralForm struct {
|
||||
Dative string `json:"dative" bson:"dative"`
|
||||
Default string `json:"default" bson:"default"`
|
||||
Genitive string `json:"genitive" bson:"genitive"`
|
||||
Accusative string `json:"accusative" bson:"accusative"`
|
||||
Instrumental string `json:"instrumental" bson:"instrumental"`
|
||||
Prepositional string `json:"prepositional" bson:"prepositional"`
|
||||
} `json:"plural_form" bson:"plural_form"`
|
||||
SingularForm struct {
|
||||
Dative string `json:"dative" bson:"dative"`
|
||||
Default string `json:"default" bson:"default"`
|
||||
Genitive string `json:"genitive" bson:"genitive"`
|
||||
Accusative string `json:"accusative" bson:"accusative"`
|
||||
Instrumental string `json:"instrumental" bson:"instrumental"`
|
||||
Prepositional string `json:"prepositional" bson:"prepositional"`
|
||||
} `json:"singular_form" bson:"singular_form"`
|
||||
} `json:"ru" bson:"ru"`
|
||||
En struct {
|
||||
SingularForm struct {
|
||||
Default string `json:"default" bson:"default"`
|
||||
} `json:"singular_form" bson:"singular_form"`
|
||||
PluralForm struct {
|
||||
Default string `json:"default" bson:"default"`
|
||||
} `json:"plural_form" bson:"plural_form"`
|
||||
Gender string `json:"gender" bson:"gender"`
|
||||
} `json:"en" bson:"en"`
|
||||
Es struct {
|
||||
SingularForm struct {
|
||||
Default string `json:"default" bson:"default"`
|
||||
} `json:"singular_form" bson:"singular_form"`
|
||||
PluralForm struct {
|
||||
Default string `json:"default" bson:"default"`
|
||||
} `json:"plural_form" bson:"plural_form"`
|
||||
Gender string `json:"gender" bson:"gender"`
|
||||
} `json:"es" bson:"es"`
|
||||
} `json:"leads" bson:"leads"`
|
||||
} `json:"entity_names" bson:"entity_names"`
|
||||
DatetimeSettings struct {
|
||||
DatePattern string `json:"date_pattern" bson:"date_pattern"`
|
||||
ShortDatePattern string `json:"short_date_pattern" bson:"short_date_pattern"`
|
||||
ShortTimePattern string `json:"short_time_pattern" bson:"short_time_pattern"`
|
||||
DateFormant string `json:"date_formant" bson:"date_formant"`
|
||||
TimeFormat string `json:"time_format" bson:"time_format"`
|
||||
Timezone string `json:"timezone" bson:"timezone"`
|
||||
TimezoneOffset string `json:"timezone_offset" bson:"timezone_offset"`
|
||||
} `json:"datetime_settings" bson:"datetime_settings"`
|
||||
} `json:"_embedded" bson:"_embedded"`
|
||||
}
|
@ -2,39 +2,19 @@ package models
|
||||
|
||||
import "time"
|
||||
|
||||
type AuthUserInformation struct {
|
||||
Email string `json:"email"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
}
|
||||
|
||||
type JWTAuthUser struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type AuthUser struct {
|
||||
type User struct {
|
||||
ID string `json:"_id"`
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
}
|
||||
|
||||
type Tokens struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
type AuthJWTDecoded struct {
|
||||
UserID string `json:"id"`
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
Login string `json:"login"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type ExchangeRequest struct {
|
||||
UserID string `json:"userId"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
const AuthJWTDecodedUserIDKey = "userID"
|
||||
|
@ -1,22 +1,5 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Audit struct {
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
Deleted bool `json:"deleted" bson:"deleted"`
|
||||
}
|
||||
|
||||
type GenerateURLResponse struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type FastifyError struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Error string `json:"error"`
|
||||
@ -28,6 +11,12 @@ type ResponseErrorHTTP struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Pagination struct {
|
||||
Page int64
|
||||
Limit int64
|
||||
}
|
||||
|
||||
const (
|
||||
BodyAuthStyle oauth2.AuthStyle = 4
|
||||
DefaultPageNumber int64 = 1
|
||||
DefaultLimit int64 = 100
|
||||
)
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"golang.org/x/oauth2"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
|
||||
)
|
||||
|
||||
@ -20,9 +19,6 @@ type HTTPConfiguration struct {
|
||||
}
|
||||
|
||||
type ServiceConfiguration struct {
|
||||
Google GoogleConfiguration
|
||||
VK VKConfiguration
|
||||
Amocrm AmocrmConfiguration
|
||||
AuthMicroservice AuthMicroserviceConfiguration
|
||||
JWT JWTConfiguration
|
||||
}
|
||||
@ -36,52 +32,10 @@ type JWTConfiguration struct {
|
||||
ExpiresIn time.Duration
|
||||
}
|
||||
|
||||
type GoogleConfiguration struct {
|
||||
ClientID string `env:"GOOGLE_CLIENT_ID,required"`
|
||||
ClientSecret string `env:"GOOGLE_CLIENT_SECRET,required"`
|
||||
OAuthConfig oauth2.Config
|
||||
URL GoogleURL
|
||||
}
|
||||
|
||||
type VKConfiguration struct {
|
||||
ClientID string `env:"VK_CLIENT_ID,required"`
|
||||
ClientSecret string `env:"VK_CLIENT_SECRET,required"`
|
||||
OAuthConfig oauth2.Config
|
||||
URL VKURL
|
||||
}
|
||||
|
||||
type AmocrmConfiguration struct {
|
||||
ClientID string `env:"AMOCRM_CLIENT_ID,required"`
|
||||
ClientSecret string `env:"AMOCRM_CLIENT_SECRET,required"`
|
||||
OAuthConfig oauth2.Config
|
||||
URL AmocrmURL
|
||||
}
|
||||
|
||||
type AuthMicroserviceConfiguration struct {
|
||||
AuthGroup string `env:"AUTH_MICROSERVICE_GROUP,required"`
|
||||
PrivateSignKey string `env:"AUTH_MICROSERVICE_PRIVATE_SIGN_KEY,required"`
|
||||
URL AuthMicroServiceURL
|
||||
}
|
||||
|
||||
type GoogleURL struct {
|
||||
Redirect string `env:"GOOGLE_REDIRECT_URL,required"`
|
||||
OAuthHost string `env:"GOOGLE_OAUTH_HOST,required"`
|
||||
UserInfo string
|
||||
}
|
||||
|
||||
type AmocrmURL struct {
|
||||
Redirect string `env:"AMOCRM_REDIRECT_URL,required"`
|
||||
OAuthHost string `env:"AMOCRM_OAUTH_HOST,required"`
|
||||
UserInfo string `env:"AMOCRM_USER_INFO_URL,required"`
|
||||
AccessToken string `env:"AMOCRM_ACCESS_TOKEN_URL,required"`
|
||||
URL AuthMicroServiceURL
|
||||
}
|
||||
|
||||
type AuthMicroServiceURL struct {
|
||||
Exchange string `env:"AUTH_MICROSERVICE_EXHANGE_URL,required"`
|
||||
Register string `env:"AUTH_MICROSERVICE_REGISTER_URL,required"`
|
||||
User string `env:"AUTH_MICROSERVICE_USER_URL,required"`
|
||||
}
|
||||
|
||||
type VKURL struct {
|
||||
Redirect string `env:"VK_REDIRECT_URL,required"`
|
||||
User string `env:"AUTH_MICROSERVICE_USER_URL,required"`
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
package models
|
||||
|
||||
type GoogleUserInformation struct {
|
||||
// The subject property contains the unique user identifier of the user who signed in
|
||||
Subject string `json:"sub" bson:"Subject"`
|
||||
Fullname string `json:"name" bson:"Fullname"`
|
||||
GivenName string `json:"given_name" bson:"GivenName"`
|
||||
FamilyName string `json:"family_name" bson:"FamilyName"`
|
||||
AvatarURL string `json:"picture" bson:"AvatarURL"`
|
||||
Email string `json:"email" bson:"Email"`
|
||||
EmailVerified bool `json:"email_verified" bson:"EmailVerified"`
|
||||
Locale string `json:"locale" bson:"Locale"`
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package models
|
||||
|
||||
type VKUserInformation struct {
|
||||
ID int64 `json:"id" bson:"id"`
|
||||
FirstName string `json:"first_name" bson:"firstname"`
|
||||
LastName string `json:"last_name" bson:"lastname"`
|
||||
Photo string `json:"photo_400_orig" bson:"avatar"`
|
||||
Sex int `json:"sex" bson:"sex"`
|
||||
Domain string `json:"domain" bson:"domain"`
|
||||
ScreenName string `json:"screen_name" bson:"screen_name"`
|
||||
Birthday string `json:"bdate" bson:"birthday"`
|
||||
PhotoID string `json:"photo_id" bson:"photo_id"`
|
||||
FollowersCount int `json:"followers_count" bson:"followers_count"`
|
||||
HomeTown string `json:"home_town" bson:"home_town"`
|
||||
Timezone float64 `json:"timezone" bson:"timezone"`
|
||||
MobilePhone string `json:"mobile_phone" bson:"mobile_phone"`
|
||||
Email string `json:"email" bson:"email"`
|
||||
}
|
179
internal/repository/account.go
Normal file
179
internal/repository/account.go
Normal file
@ -0,0 +1,179 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
|
||||
)
|
||||
|
||||
type AccountRepositoryDeps struct {
|
||||
MongoDB *mongo.Collection
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type AccountRepository struct {
|
||||
mongoDB *mongo.Collection
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewAccountRepository(deps *AccountRepositoryDeps) *AccountRepository {
|
||||
return &AccountRepository{
|
||||
mongoDB: deps.MongoDB,
|
||||
logger: deps.Logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *AccountRepository) FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) {
|
||||
filter := bson.M{
|
||||
fields.Account.UserID: id,
|
||||
fields.Account.Deleted: false,
|
||||
}
|
||||
|
||||
account, err := mongoWrapper.FindOne[models.Account](ctx, &mongoWrapper.RequestSettings{
|
||||
Driver: receiver.mongoDB,
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to find account by userID on <FindByUserID> of <AccountRepository>",
|
||||
zap.String("id", id),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
findError := errors.New(
|
||||
fmt.Errorf("failed to find account with <%s> on <FindByUserID> of <AccountRepository>: %w", id, err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
findError.SetType(errors.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil, findError
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (receiver *AccountRepository) FindMany(ctx context.Context, page, limit int64) ([]models.Account, errors.Error) {
|
||||
filter := bson.M{fields.Account.Deleted: false}
|
||||
findOptions := options.Find()
|
||||
|
||||
skip := (page - 1) * limit
|
||||
|
||||
findOptions.SetSkip(skip)
|
||||
findOptions.SetLimit(limit)
|
||||
|
||||
accounts, err := mongoWrapper.Find[models.Account](ctx, &mongoWrapper.RequestSettings{
|
||||
Driver: receiver.mongoDB,
|
||||
Options: findOptions,
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to find many accounts on <FindMany> of <AccountRepository>",
|
||||
zap.Int64("page", page),
|
||||
zap.Int64("limit", limit),
|
||||
zap.Int64("skip", skip),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("failed to find many accounts on <FindMany> of <AccountRepository>: %w", err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (receiver *AccountRepository) Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error) {
|
||||
account.CreatedAt = time.Now()
|
||||
account.UpdatedAt = time.Now()
|
||||
account.Deleted = false
|
||||
|
||||
result, err := receiver.mongoDB.InsertOne(ctx, account)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to insert account on <Insert> of <AccountRepository>",
|
||||
zap.Any("account", account),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("failed to insert account on <Insert> of <AccountRepository>: %w", err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
}
|
||||
|
||||
insertedID := result.InsertedID.(primitive.ObjectID).Hex()
|
||||
account.ID = insertedID
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (receiver *AccountRepository) Remove(ctx context.Context, id string) (*models.Account, errors.Error) {
|
||||
account := models.Account{}
|
||||
|
||||
update := bson.M{"$set": bson.M{fields.Account.Deleted: true}}
|
||||
filter := bson.M{
|
||||
fields.Account.UserID: id,
|
||||
fields.Account.Deleted: false,
|
||||
}
|
||||
|
||||
if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update).Decode(&account); err != nil {
|
||||
receiver.logger.Error("failed to set 'deleted=true' on <Delete> of <AccountRepository>",
|
||||
zap.String("id", id),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
removeErr := errors.New(
|
||||
fmt.Errorf("failed to remove account with <%s> on <Remove> of <AccountRepository>: %w", id, err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
removeErr.SetType(errors.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil, removeErr
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func (receiver *AccountRepository) Delete(ctx context.Context, id string) (*models.Account, errors.Error) {
|
||||
account := models.Account{}
|
||||
|
||||
filter := bson.M{
|
||||
fields.Account.UserID: id,
|
||||
fields.Account.Deleted: false,
|
||||
}
|
||||
|
||||
if err := receiver.mongoDB.FindOneAndDelete(ctx, filter).Decode(&account); err != nil {
|
||||
receiver.logger.Error("failed delete account on <Delete> of <AccountRepository>",
|
||||
zap.String("id", id),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
removeErr := errors.New(
|
||||
fmt.Errorf("failed to remove account with <%s> on <Delete> of <AccountRepository>: %w", id, err),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
removeErr.SetType(errors.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil, removeErr
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
|
||||
)
|
||||
|
||||
type AmocrmRepository struct {
|
||||
mongoDB *mongo.Collection
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewAmocrmRepository(mongoDB *mongo.Collection, logger *logrus.Logger) *AmocrmRepository {
|
||||
return &AmocrmRepository{
|
||||
mongoDB: mongoDB,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *AmocrmRepository) FindByID(ctx context.Context, amocrmID string) (*models.AmocrmUser, error) {
|
||||
filter := bson.M{
|
||||
fields.AmocrmUser.AmocrmID: amocrmID,
|
||||
fields.Audit.Deleted: false,
|
||||
}
|
||||
|
||||
user, err := mongoWrapper.FindOne[models.AmocrmUser](ctx, &mongoWrapper.RequestSettings{
|
||||
Driver: receiver.mongoDB,
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Errorf("failed to find amocrm user <%s> on <FindByID> of <AmocrmRepository>: %v", amocrmID, err)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, errors.ErrNoRecord
|
||||
}
|
||||
|
||||
return nil, errors.ErrFindRecord
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (receiver *AmocrmRepository) FindByUserID(ctx context.Context, userID string) (*models.AmocrmUser, error) {
|
||||
filter := bson.M{
|
||||
fields.AmocrmUser.UserID: userID,
|
||||
fields.Audit.Deleted: false,
|
||||
}
|
||||
|
||||
user, err := mongoWrapper.FindOne[models.AmocrmUser](ctx, &mongoWrapper.RequestSettings{
|
||||
Driver: receiver.mongoDB,
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Errorf("failed to find amocrm user <%s> on <FindByUserID> of <AmocrmRepository>: %v", userID, err)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, errors.ErrNoRecord
|
||||
}
|
||||
|
||||
return nil, errors.ErrFindRecord
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (receiver *AmocrmRepository) Insert(ctx context.Context, user *models.AmocrmUser) (*models.AmocrmUser, error) {
|
||||
user.Audit = models.Audit{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Deleted: false,
|
||||
}
|
||||
|
||||
result, err := receiver.mongoDB.InsertOne(ctx, user)
|
||||
if err != nil {
|
||||
receiver.logger.Errorf("failed to insert record on <Insert> of <AmocrmRepository>: %v", err)
|
||||
return nil, errors.ErrInsertRecord
|
||||
}
|
||||
|
||||
insertedID := result.InsertedID.(primitive.ObjectID).Hex()
|
||||
userCopy := *user
|
||||
userCopy.ID = insertedID
|
||||
|
||||
return &userCopy, nil
|
||||
}
|
||||
|
||||
func (receiver *AmocrmRepository) Delete(ctx context.Context, amocrmID string) (*models.AmocrmUser, error) {
|
||||
user := models.AmocrmUser{}
|
||||
|
||||
update := bson.M{"$set": bson.M{fields.Audit.Deleted: true}}
|
||||
filter := bson.M{
|
||||
fields.AmocrmUser.AmocrmID: amocrmID,
|
||||
fields.Audit.Deleted: false,
|
||||
}
|
||||
|
||||
if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update).Decode(&user); err != nil {
|
||||
receiver.logger.Errorf("failed to set 'deleted=true' with id <%s> on <Delete> of <AmocrmRepository>: %v", amocrmID, err)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, errors.ErrNoRecord
|
||||
}
|
||||
|
||||
return nil, errors.ErrUpdateRecord
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (receiver *AmocrmRepository) Remove(ctx context.Context, id string) (*models.AmocrmUser, error) {
|
||||
objectID, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
receiver.logger.Errorf("failed to parse ObjectID <%s> on <FindByID> of <DiscountRepository>: %v", id, err)
|
||||
return nil, errors.ErrInvalidArgs
|
||||
}
|
||||
|
||||
user := models.AmocrmUser{}
|
||||
|
||||
filter := bson.M{
|
||||
fields.AmocrmUser.ID: objectID,
|
||||
fields.Audit.Deleted: false,
|
||||
}
|
||||
|
||||
if err := receiver.mongoDB.FindOneAndDelete(ctx, filter).Decode(&user); err != nil {
|
||||
receiver.logger.Errorf("failed remove user with _id <%s> on <Remove> of <AmocrmRepository>: %v", id, err)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, errors.ErrNoRecord
|
||||
}
|
||||
|
||||
return nil, errors.ErrUpdateRecord
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
)
|
||||
|
||||
type GoogleRepository struct {
|
||||
mongoDB *mongo.Collection
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewGoogleRepository(mongoDB *mongo.Collection, logger *logrus.Logger) *GoogleRepository {
|
||||
return &GoogleRepository{
|
||||
mongoDB: mongoDB,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *GoogleRepository) UpdateUser(ctx context.Context, user *models.GoogleUserInformation) (*models.GoogleUserInformation, error) {
|
||||
var googleUserInformation models.GoogleUserInformation
|
||||
|
||||
updateOptions := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)
|
||||
filter := bson.D{{Key: "sub", Value: user.Subject}}
|
||||
update := bson.D{{Key: "$set", Value: user}}
|
||||
|
||||
if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, updateOptions).Decode(&googleUserInformation); err != nil {
|
||||
receiver.logger.Errorf("failed decode google user information: %v", err)
|
||||
|
||||
return nil, errors.ErrDecodeRecord
|
||||
}
|
||||
|
||||
return &googleUserInformation, nil
|
||||
}
|
||||
|
||||
func (receiver *GoogleRepository) FindUserBySubject(_ context.Context, _ string) (*models.GoogleUserInformation, error) {
|
||||
return nil, errors.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
func (receiver *GoogleRepository) InsertUser(_ context.Context, _ *models.GoogleUserInformation) (*models.GoogleUserInformation, error) {
|
||||
return nil, errors.ErrMethodNotImplemented
|
||||
}
|
@ -6,32 +6,43 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/brpaz/echozap"
|
||||
oapiMiddleware "github.com/deepmap/oapi-codegen/pkg/middleware"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/getkin/kin-openapi/openapi3filter"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/sirupsen/logrus"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Swagger *openapi3.T
|
||||
JWTUtil *utils.JWT[models.AuthJWTDecoded]
|
||||
}
|
||||
|
||||
type HTTP struct {
|
||||
logger *logrus.Entry
|
||||
logger *zap.Logger
|
||||
server *http.Server
|
||||
echo *echo.Echo
|
||||
}
|
||||
|
||||
func New(logger *logrus.Logger, swagger *openapi3.T) *HTTP {
|
||||
func New(deps *Deps) *HTTP {
|
||||
echo := echo.New()
|
||||
|
||||
echo.Use(middleware.Logger())
|
||||
echo.Use(echozap.ZapLogger(deps.Logger))
|
||||
echo.Use(middleware.Recover())
|
||||
echo.Use(oapiMiddleware.OapiRequestValidator(swagger))
|
||||
echo.Use(oapiMiddleware.OapiRequestValidator(deps.Swagger))
|
||||
echo.Use(oapiMiddleware.OapiRequestValidatorWithOptions(deps.Swagger, &oapiMiddleware.Options{
|
||||
Options: openapi3filter.Options{AuthenticationFunc: utils.NewAuthenticator(deps.JWTUtil)},
|
||||
}))
|
||||
|
||||
return &HTTP{
|
||||
echo: echo,
|
||||
logger: logrus.NewEntry(logger),
|
||||
logger: deps.Logger,
|
||||
server: &http.Server{
|
||||
Handler: echo,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
@ -51,15 +62,15 @@ func (receiver *HTTP) Run(config *models.HTTPConfiguration) {
|
||||
connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
startServerMessage := fmt.Sprintf("starting http server on %s", connectionString)
|
||||
|
||||
receiver.logger.Infoln(startServerMessage)
|
||||
receiver.logger.Info(startServerMessage)
|
||||
|
||||
if err := receiver.Listen(connectionString); err != nil && err != http.ErrServerClosed {
|
||||
receiver.logger.Infoln("http listen error: ", err)
|
||||
receiver.logger.Error("http listen error: ", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *HTTP) Stop(ctx context.Context) error {
|
||||
receiver.logger.Infoln("shutting down server ...")
|
||||
receiver.logger.Info("shutting down server...")
|
||||
|
||||
if err := receiver.server.Shutdown(ctx); err != nil {
|
||||
return fmt.Errorf("failed to shutdown server: %w", err)
|
||||
@ -68,8 +79,8 @@ func (receiver *HTTP) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (receiver *HTTP) Register(controllers *initialize.Controllers) *HTTP {
|
||||
swagger.RegisterHandlers(receiver.echo, nil)
|
||||
func (receiver *HTTP) Register(api *swagger.API) *HTTP {
|
||||
swagger.RegisterHandlers(receiver.echo, api)
|
||||
|
||||
return receiver
|
||||
}
|
||||
|
157
internal/service/account/account.go
Normal file
157
internal/service/account/account.go
Normal file
@ -0,0 +1,157 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
)
|
||||
|
||||
type accountRepository interface {
|
||||
FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error)
|
||||
FindMany(ctx context.Context, page, limit int64) ([]models.Account, errors.Error)
|
||||
Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error)
|
||||
Remove(ctx context.Context, id string) (*models.Account, errors.Error)
|
||||
Delete(ctx context.Context, id string) (*models.Account, errors.Error)
|
||||
}
|
||||
|
||||
type authClient interface {
|
||||
GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Repository accountRepository
|
||||
AuthClient authClient
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
logger *zap.Logger
|
||||
repository accountRepository
|
||||
authClient authClient
|
||||
}
|
||||
|
||||
func New(deps *Deps) *Service {
|
||||
return &Service{
|
||||
logger: deps.Logger,
|
||||
repository: deps.Repository,
|
||||
authClient: deps.AuthClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *Service) GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) {
|
||||
account, err := receiver.repository.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to get account by id on <GetAccountByUserID> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models.Pagination) ([]models.Account, errors.Error) {
|
||||
if pagination == nil {
|
||||
return nil, errors.New(
|
||||
fmt.Errorf("pagination is nil on <GetAccountsList> of <AccountService>: %w", errors.ErrInternalError),
|
||||
errors.ErrInternalError,
|
||||
)
|
||||
}
|
||||
|
||||
accounts, err := receiver.repository.FindMany(ctx, pagination.Page, pagination.Limit)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to get accounts list on <GetAccountsList> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.Int64("page", pagination.Page),
|
||||
zap.Int64("limit", pagination.Limit),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (receiver *Service) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) {
|
||||
accounts, err := receiver.repository.Insert(ctx, account)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to create account on <CreateAccount> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.Any("account", account),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
/*
|
||||
CreateAccountByUserID
|
||||
|
||||
TODO: Дополнить проверку на дефолтное значение Currency
|
||||
*/
|
||||
func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) {
|
||||
user, err := receiver.authClient.GetUser(ctx, userID)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to get user on <CreateAccountByUserID> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
account, err := receiver.repository.Insert(ctx, &models.Account{
|
||||
UserID: user.ID,
|
||||
Cart: make([]string, 0),
|
||||
Wallet: models.Wallet{
|
||||
Cash: 0,
|
||||
Money: 0,
|
||||
Currency: "",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to create account on <CreateAccountByUserID> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (receiver *Service) RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) {
|
||||
account, err := receiver.repository.Remove(ctx, userID)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to remove account on <RemoveAccount> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (receiver *Service) DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error) {
|
||||
account, err := receiver.repository.Delete(ctx, userID)
|
||||
if err != nil {
|
||||
receiver.logger.Error("failed to delete account on <DeleteAccount> of <AccountService>",
|
||||
zap.Error(err.Extract()),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
@ -30,11 +30,11 @@ type ServerInterface interface {
|
||||
// (POST /account)
|
||||
AddAccount(ctx echo.Context) error
|
||||
// Удалить аккаунт по айди
|
||||
// (DELETE /account/{accountId})
|
||||
DeleteDirectAccount(ctx echo.Context, accountId string) error
|
||||
// Получить аккаунт по ID
|
||||
// (GET /account/{accountId})
|
||||
GetDirectAccount(ctx echo.Context, accountId string) error
|
||||
// (DELETE /account/{userId})
|
||||
DeleteDirectAccount(ctx echo.Context, userId string) error
|
||||
// Получить аккаунт по ID пользователя системы единой авторизации
|
||||
// (GET /account/{userId})
|
||||
GetDirectAccount(ctx echo.Context, userId string) error
|
||||
// списко аккаунтов с пагинацией
|
||||
// (GET /accounts)
|
||||
PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error
|
||||
@ -111,36 +111,36 @@ func (w *ServerInterfaceWrapper) AddAccount(ctx echo.Context) error {
|
||||
// DeleteDirectAccount converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) DeleteDirectAccount(ctx echo.Context) error {
|
||||
var err error
|
||||
// ------------- Path parameter "accountId" -------------
|
||||
var accountId string
|
||||
// ------------- Path parameter "userId" -------------
|
||||
var userId string
|
||||
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "accountId", runtime.ParamLocationPath, ctx.Param("accountId"), &accountId)
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "userId", runtime.ParamLocationPath, ctx.Param("userId"), &userId)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter accountId: %s", err))
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err))
|
||||
}
|
||||
|
||||
ctx.Set(BearerScopes, []string{""})
|
||||
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.DeleteDirectAccount(ctx, accountId)
|
||||
err = w.Handler.DeleteDirectAccount(ctx, userId)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDirectAccount converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) GetDirectAccount(ctx echo.Context) error {
|
||||
var err error
|
||||
// ------------- Path parameter "accountId" -------------
|
||||
var accountId string
|
||||
// ------------- Path parameter "userId" -------------
|
||||
var userId string
|
||||
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "accountId", runtime.ParamLocationPath, ctx.Param("accountId"), &accountId)
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "userId", runtime.ParamLocationPath, ctx.Param("userId"), &userId)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter accountId: %s", err))
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err))
|
||||
}
|
||||
|
||||
ctx.Set(BearerScopes, []string{""})
|
||||
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.GetDirectAccount(ctx, accountId)
|
||||
err = w.Handler.GetDirectAccount(ctx, userId)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -224,6 +224,8 @@ func (w *ServerInterfaceWrapper) GetCurrencies(ctx echo.Context) error {
|
||||
func (w *ServerInterfaceWrapper) UpdateCurrencies(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
ctx.Set(BearerScopes, []string{""})
|
||||
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.UpdateCurrencies(ctx)
|
||||
return err
|
||||
@ -237,18 +239,18 @@ func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error {
|
||||
|
||||
// Parameter object where we will unmarshal all parameters from the context
|
||||
var params GetHistoryParams
|
||||
// ------------- Optional query parameter "p" -------------
|
||||
// ------------- Optional query parameter "page" -------------
|
||||
|
||||
err = runtime.BindQueryParameter("form", false, false, "p", ctx.QueryParams(), ¶ms.P)
|
||||
err = runtime.BindQueryParameter("form", false, false, "page", ctx.QueryParams(), ¶ms.Page)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: %s", err))
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter page: %s", err))
|
||||
}
|
||||
|
||||
// ------------- Optional query parameter "s" -------------
|
||||
// ------------- Optional query parameter "limit" -------------
|
||||
|
||||
err = runtime.BindQueryParameter("form", false, false, "s", ctx.QueryParams(), ¶ms.S)
|
||||
err = runtime.BindQueryParameter("form", false, false, "limit", ctx.QueryParams(), ¶ms.Limit)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter s: %s", err))
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err))
|
||||
}
|
||||
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
@ -340,8 +342,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
|
||||
router.DELETE(baseURL+"/account", wrapper.DeleteAccount)
|
||||
router.GET(baseURL+"/account", wrapper.GetAccount)
|
||||
router.POST(baseURL+"/account", wrapper.AddAccount)
|
||||
router.DELETE(baseURL+"/account/:accountId", wrapper.DeleteDirectAccount)
|
||||
router.GET(baseURL+"/account/:accountId", wrapper.GetDirectAccount)
|
||||
router.DELETE(baseURL+"/account/:userId", wrapper.DeleteDirectAccount)
|
||||
router.GET(baseURL+"/account/:userId", wrapper.GetDirectAccount)
|
||||
router.GET(baseURL+"/accounts", wrapper.PaginationAccounts)
|
||||
router.DELETE(baseURL+"/cart", wrapper.RemoveFromCart)
|
||||
router.PATCH(baseURL+"/cart", wrapper.Add2cart)
|
||||
@ -359,64 +361,66 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
|
||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"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",
|
||||
"PxAmwKsLq/8LAAD//9ctKcoUQwAA",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
@ -1,4 +1,97 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
//go:generate oapi-codegen --config api.yaml ../../openapi.yaml
|
||||
//go:generate oapi-codegen --config models.yaml ../../openapi.yaml
|
||||
|
||||
type AccountController interface {
|
||||
RemoveAccount(ctx echo.Context) error
|
||||
GetAccount(ctx echo.Context) error
|
||||
CreateAccount(ctx echo.Context) error
|
||||
RemoveDirectAccount(ctx echo.Context, userId string) error
|
||||
GetDirectAccount(ctx echo.Context, userId string) error
|
||||
GetAccounts(ctx echo.Context, params PaginationAccountsParams) error
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
AccountController AccountController
|
||||
}
|
||||
|
||||
type API struct {
|
||||
accountController AccountController
|
||||
}
|
||||
|
||||
func New(deps *Deps) *API {
|
||||
return &API{
|
||||
accountController: deps.AccountController,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *API) DeleteAccount(ctx echo.Context) error {
|
||||
return receiver.accountController.RemoveAccount(ctx)
|
||||
}
|
||||
|
||||
func (receiver *API) GetAccount(ctx echo.Context) error {
|
||||
return receiver.accountController.GetAccount(ctx)
|
||||
}
|
||||
|
||||
func (receiver *API) AddAccount(ctx echo.Context) error {
|
||||
return receiver.accountController.CreateAccount(ctx)
|
||||
}
|
||||
|
||||
func (receiver *API) DeleteDirectAccount(ctx echo.Context, accountId string) error {
|
||||
return receiver.accountController.RemoveDirectAccount(ctx, accountId)
|
||||
}
|
||||
|
||||
func (receiver *API) GetDirectAccount(ctx echo.Context, accountId string) error {
|
||||
return receiver.accountController.GetDirectAccount(ctx, accountId)
|
||||
}
|
||||
|
||||
func (receiver *API) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error {
|
||||
return receiver.accountController.GetAccounts(ctx, params)
|
||||
}
|
||||
|
||||
func (receiver *API) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) Add2cart(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) PayCart(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) GetCurrencies(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) UpdateCurrencies(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) GetHistory(ctx echo.Context, params GetHistoryParams) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) Add2history(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) RequestMoney(ctx echo.Context, params RequestMoneyParams) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) ChangeCurrency(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
||||
func (receiver *API) PutMoney(ctx echo.Context) error {
|
||||
return ctx.String(http.StatusNotImplemented, "method not implemented")
|
||||
}
|
||||
|
@ -40,17 +40,17 @@ type Error struct {
|
||||
|
||||
// History defines model for History.
|
||||
type History struct {
|
||||
Comment *string `json:"comment,omitempty"`
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
||||
Deleted *bool `json:"deleted,omitempty"`
|
||||
Comment string `json:"comment"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Deleted bool `json:"deleted"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
Id *string `json:"id,omitempty"`
|
||||
Id string `json:"id"`
|
||||
|
||||
// Subject Я пока не могу предположить, какие будут фильтры по истории, поэтому предлагаю в это поле просто класть строку с json. ибо для каждого типа записи она своя.
|
||||
Subject *string `json:"subject,omitempty"`
|
||||
Type *HistoryType `json:"type,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
UserId *string `json:"userId,omitempty"`
|
||||
// Subject Я пока не могу предположить, какие будут фильтры по истории, поэтому предлагаю в это поле просто класть строку с json. Ибо для каждого типа записи она своя.
|
||||
Subject string `json:"subject"`
|
||||
Type HistoryType `json:"type"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
// HistoryType defines model for History.Type.
|
||||
@ -58,11 +58,14 @@ type HistoryType string
|
||||
|
||||
// Wallet defines model for Wallet.
|
||||
type Wallet struct {
|
||||
Cash *int64 `json:"cash,omitempty"`
|
||||
Currency *string `json:"currency,omitempty"`
|
||||
// Cash Сумма money переведённая на текущий курс
|
||||
Cash int64 `json:"cash"`
|
||||
|
||||
// Money деньги на счету в копейках. Чтобы при перессчётах не возникало денег изниоткуда. фиксируемся к одной валюте, она будет внутренней, никому её не покажем
|
||||
Money *int64 `json:"money,omitempty"`
|
||||
// Currency Текущий курс валюты
|
||||
Currency string `json:"currency"`
|
||||
|
||||
// Money Деньги на счету в копейках. Чтобы при перессчётах не возникало денег изниоткуда. фиксируемся к одной валюте, она будет внутренней, никому её не покажем
|
||||
Money int64 `json:"money"`
|
||||
}
|
||||
|
||||
// PaginationAccountsParams defines parameters for PaginationAccounts.
|
||||
@ -81,13 +84,13 @@ type RemoveFromCartParams struct {
|
||||
|
||||
// Add2cartJSONBody defines parameters for Add2cart.
|
||||
type Add2cartJSONBody struct {
|
||||
Id *string `json:"id,omitempty"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// PayCartJSONBody defines parameters for PayCart.
|
||||
type PayCartJSONBody struct {
|
||||
// UserId ID для того, чтобы указать сервису оплаты, какой юзер оплатил
|
||||
UserId *string `json:"userId,omitempty"`
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
// UpdateCurrenciesJSONBody defines parameters for UpdateCurrencies.
|
||||
@ -95,29 +98,29 @@ type UpdateCurrenciesJSONBody = []string
|
||||
|
||||
// GetHistoryParams defines parameters for GetHistory.
|
||||
type GetHistoryParams struct {
|
||||
// P Номер страницы, начиная с 1
|
||||
P *int `form:"p,omitempty" json:"p,omitempty"`
|
||||
// Page Номер страницы, начиная с 1
|
||||
Page *int `form:"page,omitempty" json:"page,omitempty"`
|
||||
|
||||
// S Размер страницы
|
||||
S *int `form:"s,omitempty" json:"s,omitempty"`
|
||||
// Limit Размер страницы
|
||||
Limit *int `form:"limit,omitempty" json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// RequestMoneyParams defines parameters for RequestMoney.
|
||||
type RequestMoneyParams struct {
|
||||
// Cash Номер страницы, начиная с 1
|
||||
// Cash Количество денег
|
||||
Cash int `form:"cash" json:"cash"`
|
||||
}
|
||||
|
||||
// ChangeCurrencyJSONBody defines parameters for ChangeCurrency.
|
||||
type ChangeCurrencyJSONBody struct {
|
||||
Currency *string `json:"currency,omitempty"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
// PutMoneyJSONBody defines parameters for PutMoney.
|
||||
type PutMoneyJSONBody struct {
|
||||
Cash *int `json:"cash,omitempty"`
|
||||
Currency *string `json:"currency,omitempty"`
|
||||
UserId *string `json:"userId,omitempty"`
|
||||
Cash int `json:"cash"`
|
||||
Currency string `json:"currency"`
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
// Add2cartJSONRequestBody defines body for Add2cart for application/json ContentType.
|
||||
|
68
internal/utils/authenticator.go
Normal file
68
internal/utils/authenticator.go
Normal file
@ -0,0 +1,68 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/middleware"
|
||||
"github.com/getkin/kin-openapi/openapi3filter"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "Bearer "
|
||||
)
|
||||
|
||||
func NewAuthenticator(jwtUtil *JWT[models.AuthJWTDecoded]) openapi3filter.AuthenticationFunc {
|
||||
return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
|
||||
if jwtUtil == nil {
|
||||
return errors.New(fmt.Errorf("jwt util is nil"), errors.ErrInvalidArgs).Extract()
|
||||
}
|
||||
|
||||
return authenticate(jwtUtil, ctx, input)
|
||||
}
|
||||
}
|
||||
|
||||
func authenticate(jwtUtil *JWT[models.AuthJWTDecoded], ctx context.Context, input *openapi3filter.AuthenticationInput) error {
|
||||
if input.SecuritySchemeName != "BearerAuth" {
|
||||
return fmt.Errorf("security scheme %s != 'BearerAuth'", input.SecuritySchemeName)
|
||||
}
|
||||
|
||||
// Now, we need to get the JWS from the request, to match the request expectations
|
||||
// against request contents.
|
||||
jws, err := parseJWSFromRequest(input.RequestValidationInput.Request)
|
||||
if err != nil {
|
||||
return err.Extract()
|
||||
}
|
||||
|
||||
// if the JWS is valid, we have a JWT, which will contain a bunch of claims.
|
||||
token, validateErr := jwtUtil.Validate(jws)
|
||||
if validateErr != nil {
|
||||
return validateErr
|
||||
}
|
||||
|
||||
// Set the property on the echo context so the handler is able to
|
||||
// access the claims data we generate in here.
|
||||
echoCtx := middleware.GetEchoContext(ctx)
|
||||
|
||||
echoCtx.Set(models.AuthJWTDecodedUserIDKey, token.UserID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extracts a JWS string from an Authorization: Bearer <jws> header
|
||||
func parseJWSFromRequest(request *http.Request) (string, errors.Error) {
|
||||
header := request.Header.Get("Authorization")
|
||||
|
||||
if header == "" || !strings.HasPrefix(header, prefix) {
|
||||
return "", errors.New(
|
||||
fmt.Errorf("failed to parse jws from request header: %s", header),
|
||||
errors.ErrNoAccess,
|
||||
)
|
||||
}
|
||||
|
||||
return strings.TrimPrefix(header, prefix), nil
|
||||
}
|
13
internal/utils/bind.go
Normal file
13
internal/utils/bind.go
Normal file
@ -0,0 +1,13 @@
|
||||
package utils
|
||||
|
||||
import "github.com/labstack/echo/v4"
|
||||
|
||||
func EchoBind[T any](ctx echo.Context) (*T, error) {
|
||||
item := new(T)
|
||||
|
||||
if err := ctx.Bind(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
@ -7,16 +7,17 @@ import (
|
||||
)
|
||||
|
||||
var clientErrors = map[int]error{
|
||||
http.StatusInternalServerError: errors.ErrInvalidReturnValue,
|
||||
http.StatusInternalServerError: errors.ErrInternalError,
|
||||
http.StatusBadRequest: errors.ErrInvalidArgs,
|
||||
http.StatusNotImplemented: errors.ErrMethodNotImplemented,
|
||||
http.StatusNotFound: errors.ErrNoServerItem,
|
||||
http.StatusNotFound: errors.ErrNotFound,
|
||||
http.StatusForbidden: errors.ErrNoAccess,
|
||||
}
|
||||
|
||||
func DetermineClientErrorResponse(statusCode int) error {
|
||||
err, ok := clientErrors[statusCode]
|
||||
if !ok {
|
||||
return errors.ErrInvalidReturnValue
|
||||
return errors.ErrInternalError
|
||||
}
|
||||
|
||||
return err
|
||||
|
@ -8,31 +8,25 @@ import (
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
)
|
||||
|
||||
var httpStatuses = map[error]int{
|
||||
errors.ErrEmptyArgs: http.StatusInternalServerError,
|
||||
errors.ErrInvalidArgs: http.StatusBadRequest,
|
||||
errors.ErrFindRecord: http.StatusInternalServerError,
|
||||
errors.ErrInsertRecord: http.StatusInternalServerError,
|
||||
errors.ErrMethodNotImplemented: http.StatusNotImplemented,
|
||||
errors.ErrNoRecord: http.StatusNotFound,
|
||||
errors.ErrNoServerItem: http.StatusNotFound,
|
||||
errors.ErrTransaction: http.StatusInternalServerError,
|
||||
errors.ErrTransactionSessionStart: http.StatusInternalServerError,
|
||||
errors.ErrUpdateRecord: http.StatusInternalServerError,
|
||||
errors.ErrInvalidReturnValue: http.StatusInternalServerError,
|
||||
var httpStatuses = map[errors.ErrorType]int{
|
||||
errors.ErrInternalError: http.StatusInternalServerError,
|
||||
errors.ErrInvalidArgs: http.StatusBadRequest,
|
||||
errors.ErrNoAccess: http.StatusForbidden,
|
||||
errors.ErrNotFound: http.StatusNotFound,
|
||||
errors.ErrMethodNotImplemented: http.StatusNotImplemented,
|
||||
}
|
||||
|
||||
func DetermineEchoErrorResponse(ctx echo.Context, err error, message string) error {
|
||||
status, ok := httpStatuses[err]
|
||||
func DetermineEchoErrorResponse(ctx echo.Context, err errors.Error) error {
|
||||
status, ok := httpStatuses[err.Type()]
|
||||
if !ok {
|
||||
return ctx.JSON(http.StatusInternalServerError, models.ResponseErrorHTTP{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Message: message,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return ctx.JSON(status, models.ResponseErrorHTTP{
|
||||
StatusCode: status,
|
||||
Message: message,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
38
internal/utils/pagination.go
Normal file
38
internal/utils/pagination.go
Normal file
@ -0,0 +1,38 @@
|
||||
package utils
|
||||
|
||||
import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
|
||||
|
||||
func DeterminePagination(page, limit interface{}) *models.Pagination {
|
||||
determinePage := func() int64 {
|
||||
if page == nil {
|
||||
return models.DefaultPageNumber
|
||||
}
|
||||
|
||||
pageNumber, isPageNumberOK := page.(int64)
|
||||
|
||||
if !isPageNumberOK || pageNumber < 1 {
|
||||
return models.DefaultPageNumber
|
||||
}
|
||||
|
||||
return pageNumber
|
||||
}
|
||||
|
||||
determineLimit := func() int64 {
|
||||
if limit == nil {
|
||||
return models.DefaultLimit
|
||||
}
|
||||
|
||||
limitNumber, isLimitNumberOK := limit.(int64)
|
||||
|
||||
if !isLimitNumberOK || limitNumber > models.DefaultLimit {
|
||||
return models.DefaultLimit
|
||||
}
|
||||
|
||||
return limitNumber
|
||||
}
|
||||
|
||||
return &models.Pagination{
|
||||
Page: determinePage(),
|
||||
Limit: determineLimit(),
|
||||
}
|
||||
}
|
@ -8,50 +8,10 @@ import (
|
||||
)
|
||||
|
||||
func ValidateConfigurationURLs(config *models.ServiceConfiguration) error {
|
||||
if err := validateGoogleURLs(&config.Google.URL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateAmocrmURLs(&config.Amocrm.URL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return validateAuthMicroserviceURLs(&config.AuthMicroservice.URL)
|
||||
}
|
||||
|
||||
func validateGoogleURLs(urls *models.GoogleURL) error {
|
||||
if !validate.URL(urls.OAuthHost) {
|
||||
return fmt.Errorf("invalid google oauth host: %s", urls.OAuthHost)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAmocrmURLs(urls *models.AmocrmURL) error {
|
||||
if !validate.URL(urls.OAuthHost) {
|
||||
return fmt.Errorf("invalid amocrm oauth host: %s", urls.OAuthHost)
|
||||
}
|
||||
|
||||
if !validate.URL(urls.UserInfo) {
|
||||
return fmt.Errorf("invalid amocrm user info url: %s", urls.UserInfo)
|
||||
}
|
||||
|
||||
if !validate.URL(urls.AccessToken) {
|
||||
return fmt.Errorf("invalid amocrm access token url: %s", urls.AccessToken)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAuthMicroserviceURLs(urls *models.AuthMicroServiceURL) error {
|
||||
if !validate.URL(urls.Exchange) {
|
||||
return fmt.Errorf("invalid auth microservice exchange url: %s", urls.Exchange)
|
||||
}
|
||||
|
||||
if !validate.URL(urls.Register) {
|
||||
return fmt.Errorf("invalid auth register url: %s", urls.Register)
|
||||
}
|
||||
|
||||
if !validate.URL(urls.User) {
|
||||
return fmt.Errorf("invalid auth user url: %s", urls.User)
|
||||
}
|
||||
|
59
openapi.yaml
59
openapi.yaml
@ -129,19 +129,19 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/Account"
|
||||
|
||||
/account/{accountId}:
|
||||
/account/{userId}:
|
||||
get:
|
||||
tags:
|
||||
- account
|
||||
summary: Получить аккаунт по ID
|
||||
summary: Получить аккаунт по ID пользователя системы единой авторизации
|
||||
description: Метод необходимый в основном только менеджерам, для получения данных о клиенте
|
||||
security:
|
||||
- Bearer: []
|
||||
operationId: getDirectAccount
|
||||
parameters:
|
||||
- name: accountId
|
||||
- name: userId
|
||||
in: path
|
||||
description: id аккаунта
|
||||
description: ID аккаунта
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
@ -173,7 +173,7 @@ paths:
|
||||
security:
|
||||
- Bearer: []
|
||||
parameters:
|
||||
- name: accountId
|
||||
- name: userId
|
||||
in: path
|
||||
description: ID аккаунта
|
||||
required: true
|
||||
@ -223,6 +223,8 @@ paths:
|
||||
- currency
|
||||
summary: обновляет список одобренных валют
|
||||
operationId: updateCurrencies
|
||||
security:
|
||||
- Bearer: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@ -249,6 +251,10 @@ paths:
|
||||
type: string
|
||||
'401':
|
||||
description: Uanuthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
/cart:
|
||||
patch:
|
||||
@ -264,6 +270,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
@ -330,11 +337,14 @@ paths:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: >-
|
||||
- Очевидно нужен воркер или очередь(место где можно потрогать кафку), которая будет слать запросы в конечные сервисы для добавления привилегий в аккаунт пользователя
|
||||
- Очевидно нужен воркер или очередь (место где можно потрогать кафку), которая будет слать запросы в конечные сервисы для добавления привилегий в аккаунт пользователя
|
||||
|
||||
- В случае если денег в кошельке достаточно - отправить в воркер или очередь задачи на разослать конкретным сервисам добавление привилегий
|
||||
|
||||
- Если денег недостаточно, получить ссылку на оплату у сервиса платёжки и вернуть её. в случае оплаты, повторить вызов этого метода
|
||||
- Если денег недостаточно, вернуть ошибку и указать
|
||||
количество недостающих средств в валюте
|
||||
|
||||
- Отправить запрос на discount сервис
|
||||
|
||||
- Очистить корзину
|
||||
|
||||
@ -343,6 +353,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [userId]
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
@ -373,9 +384,9 @@ paths:
|
||||
|
||||
- Отвалидировать, что такая валюта одобрена
|
||||
|
||||
- Получить данные из сервиса cbrf(выдам задачу на него чуть позднее)
|
||||
- Получить данные из сервиса cbrf (выдам задачу на него чуть позднее)
|
||||
|
||||
- Перевести валюту кошелька в нвоую валюту. кошелёк нарочно целочисленный, чтобы не было претензий к точности с плавающей точкой, поэтому, например долларовый счёт считается в центах
|
||||
- Перевести валюту кошелька в новую валюту. Кошелёк нарочно целочисленный, чтобы не было претензий к точности с плавающей точкой, поэтому, например долларовый счёт считается в центах
|
||||
|
||||
security:
|
||||
- Bearer: []
|
||||
@ -384,6 +395,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [currency]
|
||||
properties:
|
||||
currency:
|
||||
type: string
|
||||
@ -426,6 +438,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [cash, currency, userId]
|
||||
properties:
|
||||
cash:
|
||||
type: integer
|
||||
@ -470,7 +483,7 @@ paths:
|
||||
parameters:
|
||||
- name: cash
|
||||
in: query
|
||||
description: Номер страницы, начиная с 1
|
||||
description: Количество денег
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
@ -502,15 +515,15 @@ paths:
|
||||
security:
|
||||
- Bearer: []
|
||||
parameters:
|
||||
- name: p
|
||||
- name: page
|
||||
in: query
|
||||
description: Номер страницы, начиная с 1
|
||||
required: false
|
||||
explode: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
- name: s
|
||||
default: 1
|
||||
- name: limit
|
||||
in: query
|
||||
description: Размер страницы
|
||||
required: false
|
||||
@ -603,22 +616,29 @@ components:
|
||||
|
||||
Wallet:
|
||||
type: object
|
||||
required: [currency, cash, money]
|
||||
properties:
|
||||
currency:
|
||||
description: Текущий курс валюты
|
||||
type: string
|
||||
example: "RUB"
|
||||
cash:
|
||||
description: Сумма money переведённая на текущий курс
|
||||
type: integer
|
||||
format: int64
|
||||
example: 10701
|
||||
money:
|
||||
type: integer
|
||||
format: int64
|
||||
description: деньги на счету в копейках. Чтобы при перессчётах не возникало денег изниоткуда. фиксируемся к одной валюте, она будет внутренней, никому её не покажем
|
||||
description: >-
|
||||
Деньги на счету в копейках. Чтобы при перессчётах не
|
||||
возникало денег изниоткуда. фиксируемся к одной валюте,
|
||||
она будет внутренней, никому её не покажем
|
||||
example: 10701.60
|
||||
|
||||
History:
|
||||
type: object
|
||||
required: [id, userId, type, deleted, createdAt, updatedAt, comment, subject]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
@ -648,16 +668,15 @@ components:
|
||||
subject:
|
||||
type: string
|
||||
description: >-
|
||||
Я пока не могу предположить, какие будут фильтры по истории, поэтому
|
||||
предлагаю в это поле просто класть строку с json. ибо для каждого типа
|
||||
записи она своя.
|
||||
Я пока не могу предположить, какие будут фильтры по
|
||||
истории, поэтому предлагаю в это
|
||||
поле просто класть строку с json.
|
||||
Ибо для каждого типа записи она своя.
|
||||
example: {"tariffs":["807f1f77bcf81cd799439011","807f1f77bcf81cd799439011"]}
|
||||
|
||||
Error:
|
||||
type: object
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
required: [code, message]
|
||||
properties:
|
||||
statusCode:
|
||||
type: integer
|
||||
|
@ -3,9 +3,8 @@ package closer
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Callback func(ctx context.Context) error
|
||||
@ -13,11 +12,10 @@ type Callback func(ctx context.Context) error
|
||||
type Closer struct {
|
||||
mutex sync.Mutex
|
||||
callbacks []Callback
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func New(logger *logrus.Logger) *Closer {
|
||||
return &Closer{logger: logger}
|
||||
func New() *Closer {
|
||||
return &Closer{}
|
||||
}
|
||||
|
||||
func (receiver *Closer) Add(callback Callback) {
|
||||
@ -36,7 +34,7 @@ func (receiver *Closer) Close(ctx context.Context) error {
|
||||
go func() {
|
||||
for index, callback := range receiver.callbacks {
|
||||
if err := callback(ctx); err != nil {
|
||||
receiver.logger.Errorf("[! (%d)] %v", index, err)
|
||||
log.Printf("[! (%d)] %v", index, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package mongo
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Configuration struct {
|
||||
@ -15,6 +16,7 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type RequestSettings struct {
|
||||
Driver *mongo.Collection
|
||||
Filter primitive.M
|
||||
Driver *mongo.Collection
|
||||
Options *options.FindOptions
|
||||
Filter primitive.M
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ func Find[T any](ctx context.Context, settings *RequestSettings) ([]T, error) {
|
||||
|
||||
results := make([]T, 0)
|
||||
|
||||
cursor, err := settings.Driver.Find(ctx, settings.Filter)
|
||||
cursor, err := settings.Driver.Find(ctx, settings.Filter, settings.Options)
|
||||
if err != nil {
|
||||
return []T{}, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user