@@ -2,14 +2,18 @@ package lavatop
22
33import (
44 "encoding/json"
5+ "fmt"
56 "github.com/golang-jwt/jwt/v4"
67 "goproxy/application"
8+ "goproxy/domain/aggregates"
79 "goproxy/domain/lavatopsubdomain/lavatopaggregates"
810 "goproxy/domain/lavatopsubdomain/lavatopvalueobjects"
911 "goproxy/infrastructure/api/api-http/google_auth"
1012 "goproxy/infrastructure/dto"
13+ "io"
1114 "log"
1215 "net/http"
16+ "os"
1317)
1418
1519type Handler struct {
@@ -18,16 +22,22 @@ type Handler struct {
1822 plansRepository application.PlanRepository
1923 planOfferRepository application.PlanOfferRepository
2024 lavaTopUseCases application.LavaTopUseCases
25+ plansResponse PlansResponse
2126}
2227
2328func NewHandler (billingService application.BillingService [lavatopaggregates.Invoice , lavatopvalueobjects.Offer ],
2429 planRepository application.PlanRepository , planOfferRepository application.PlanOfferRepository ,
25- lavaTopUseCases application.LavaTopUseCases ) * Handler {
30+ lavaTopUseCases application.LavaTopUseCases , userUseCases application.UserUseCases ) * Handler {
31+
32+ plansResponse := NewPlansResponse (planRepository , lavaTopUseCases , planOfferRepository )
33+
2634 return & Handler {
2735 billingService : billingService ,
2836 plansRepository : planRepository ,
2937 planOfferRepository : planOfferRepository ,
3038 lavaTopUseCases : lavaTopUseCases ,
39+ plansResponse : plansResponse ,
40+ userUseCases : userUseCases ,
3141 }
3242}
3343
@@ -109,97 +119,131 @@ func (h Handler) GetInvoices(w http.ResponseWriter, r *http.Request) {
109119 panic ("not implemented" )
110120}
111121
112- func (h Handler ) PostInvoices (writer http.ResponseWriter , request * http.Request ) {
113- panic ("not implemented" )
114- }
122+ func (h Handler ) PostInvoice (w http.ResponseWriter , r * http.Request ) {
123+ user , userErr := h .getUser (r )
124+ if userErr != nil {
125+ w .WriteHeader (http .StatusUnauthorized )
126+ _ = json .NewEncoder (w ).Encode (dto.ApiResponse [dto.PostInvoiceResponse ]{
127+ Payload : nil ,
128+ ErrorCode : http .StatusUnauthorized ,
129+ ErrorMessage : "not authorized" ,
130+ })
131+ return
132+ }
115133
116- func (h Handler ) GetPlans (w http.ResponseWriter , _ * http.Request ) {
117- response := dto.ApiResponse [[]dto.Plan ]{
118- Payload : nil ,
119- ErrorCode : 0 ,
120- ErrorMessage : "" ,
134+ var cmd dto.AccountingIssueInvoiceCommand
135+ decoder := json .NewDecoder (http .MaxBytesReader (w , r .Body , 512 ))
136+ if err := decoder .Decode (& cmd ); err != nil {
137+ w .WriteHeader (http .StatusBadRequest )
138+ _ = json .NewEncoder (w ).Encode (dto.ApiResponse [dto.PostInvoiceResponse ]{
139+ Payload : nil ,
140+ ErrorCode : http .StatusBadRequest ,
141+ ErrorMessage : "invalid body" ,
142+ })
143+ return
121144 }
122145
123- plans , plansErr := h .plansRepository .GetAllWithFeatures ()
124- if plansErr != nil {
125- response .ErrorCode = http .StatusInternalServerError
126- response .ErrorMessage = "could not load plans"
127- w .WriteHeader (http .StatusInternalServerError )
146+ currency , currencyErr := lavatopvalueobjects .ParseCurrency (cmd .Currency )
147+ if currencyErr != nil {
148+ w .WriteHeader (http .StatusBadRequest )
149+ _ = json .NewEncoder (w ).Encode (dto.ApiResponse [dto.PostInvoiceResponse ]{
150+ Payload : nil ,
151+ ErrorCode : http .StatusBadRequest ,
152+ ErrorMessage : "invalid currency" ,
153+ })
154+ return
155+ }
156+
157+ paymentMethod , paymentMethodErr := lavatopvalueobjects .ParsePaymentMethod (cmd .PaymentMethod )
158+ if paymentMethodErr != nil {
159+ w .WriteHeader (http .StatusBadRequest )
160+ _ = json .NewEncoder (w ).Encode (dto.ApiResponse [dto.PostInvoiceResponse ]{
161+ Payload : nil ,
162+ ErrorCode : http .StatusBadRequest ,
163+ ErrorMessage : "invalid payment method" ,
164+ })
165+ return
166+ }
167+
168+ newIssueInvoiceResponse := NewIssueInvoiceResponse (h .lavaTopUseCases , user , currency , paymentMethod , cmd .OfferId )
169+
170+ response , responseErr := newIssueInvoiceResponse .Build ()
171+ if responseErr != nil {
128172 _ = json .NewEncoder (w ).Encode (response )
129173 return
130174 }
131175
132- planFeatures := make (map [int ][]string )
133- for _ , plan := range plans {
134- features := make ([]string , len (plan .Features ()))
135- for fi , feature := range plan .Features () {
136- features [fi ] = feature .Feature ()
137- }
138- planFeatures [plan .Id ()] = features
139- }
140-
141- planPrices := make (map [int ][]dto.Price )
142- lavatopOffers , lavatopOffersErr := h .lavaTopUseCases .GetOffers ()
143- if lavatopOffersErr == nil {
144-
145- for _ , plan := range plans {
146- planOfferIds , offersErr := h .planOfferRepository .GetOffers (plan .Id ())
147- if offersErr != nil {
148- continue
149- }
150-
151- for _ , offer := range lavatopOffers {
152- for _ , planOffers := range planOfferIds {
153- if offer .ExtId () == planOffers .OfferId () {
154- for _ , v := range offer .Prices () {
155- priceDto := dto.Price {
156- Currency : v .Currency ().String (),
157- Cents : v .Cents (),
158- }
159- planPrices [plan .Id ()] = append (planPrices [plan .Id ()], priceDto )
160- }
161- }
162- }
163- }
164- }
165- }
166-
167- planResponses := make ([]dto.Plan , len (plans ))
168- for i , plan := range plans {
169- features := make ([]dto.Feature , len (plan .Features ()))
170- for fi , feature := range plan .Features () {
171- features [fi ] = dto.Feature {
172- Feature : feature .Feature (),
173- FeatureDescription : feature .Description (),
174- }
175- }
176-
177- planResponses [i ] = dto.Plan {
178- Name : plan .Name (),
179- Limits : dto.Limits {
180- Bandwidth : dto.BandwidthLimit {
181- IsLimited : plan .LimitBytes () != 0 ,
182- Used : 0 ,
183- Total : plan .LimitBytes (),
184- },
185- Connections : dto.ConnectionLimit {
186- IsLimited : true ,
187- MaxConcurrentConnections : 25 ,
188- },
189- Speed : dto.SpeedLimit {
190- IsLimited : false ,
191- MaxBytesPerSecond : 125_000_000 , // 125_000_000 bytes is 1 Gigabit/s
192- },
193- },
194- Features : features ,
195- DurationDays : plan .DurationDays (),
196- Prices : planPrices [plan .Id ()],
197- }
198- }
199-
200- response .Payload = & planResponses
176+ w .Header ().Set ("Content-Type" , "application/json" )
177+ w .WriteHeader (http .StatusOK )
178+ _ = json .NewEncoder (w ).Encode (response )
179+ }
180+
181+ func (h Handler ) GetPlans (w http.ResponseWriter , _ * http.Request ) {
182+ response , responseErr := h .plansResponse .Build ()
183+ if responseErr != nil {
184+ w .WriteHeader (http .StatusInternalServerError )
185+ _ = json .NewEncoder (w ).Encode (dto.ApiResponse [[]dto.Plan ]{
186+ Payload : nil ,
187+ ErrorCode : http .StatusInternalServerError ,
188+ ErrorMessage : "could not load plans" ,
189+ })
190+ return
191+ }
201192
202193 w .Header ().Set ("Content-Type" , "application/json" )
203194 w .WriteHeader (http .StatusOK )
204195 _ = json .NewEncoder (w ).Encode (response )
205196}
197+
198+ func (h Handler ) getUser (r * http.Request ) (aggregates.User , error ) {
199+ idToken , err := google_auth .GetIdTokenFromCookie (r )
200+ if err != nil {
201+ return aggregates.User {}, err
202+ }
203+
204+ verifiedToken , err := google_auth .VerifyIDToken (idToken )
205+ if err != nil {
206+ return aggregates.User {}, fmt .Errorf ("failed to verify token: %w" , err )
207+ }
208+
209+ claims , ok := verifiedToken .Claims .(jwt.MapClaims )
210+ if ! ok {
211+ return aggregates.User {}, fmt .Errorf ("failed to parse token claims" )
212+ }
213+
214+ email := claims ["email" ].(string )
215+ if email == "" {
216+ return aggregates.User {}, fmt .Errorf ("email claim empty" )
217+ }
218+
219+ usersApiHost := os .Getenv ("USERS_API_HOST" )
220+ if usersApiHost == "" {
221+ return aggregates.User {}, fmt .Errorf ("users api host empty" )
222+ }
223+
224+ resp , err := http .Get (fmt .Sprintf ("%s/users/get?email=%s" , usersApiHost , email ))
225+ if err != nil {
226+ return aggregates.User {}, fmt .Errorf ("failed to fetch user id: %v" , err )
227+ }
228+ defer func (Body io.ReadCloser ) {
229+ _ = Body .Close ()
230+ }(resp .Body )
231+
232+ body , bodyErr := io .ReadAll (resp .Body )
233+ if bodyErr != nil {
234+ return aggregates.User {}, fmt .Errorf ("failed to read response body: %v" , err )
235+ }
236+
237+ var userResult dto.GetUserResult
238+ deserializationErr := json .Unmarshal (body , & userResult )
239+ if deserializationErr != nil {
240+ return aggregates.User {}, fmt .Errorf ("failed to deserialize user result: %v" , deserializationErr )
241+ }
242+
243+ user , userErr := aggregates .NewUser (userResult .Id , userResult .Username , email , email )
244+ if userErr != nil {
245+ return aggregates.User {}, fmt .Errorf ("failed to load user: %v" , userErr )
246+ }
247+
248+ return user , nil
249+ }
0 commit comments