Skip to content

Commit

Permalink
add delete user endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
rawdaGastan committed Dec 25, 2024
1 parent 4a180e7 commit 8bde0ec
Show file tree
Hide file tree
Showing 23 changed files with 800 additions and 166 deletions.
2 changes: 2 additions & 0 deletions client/src/components/Toast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { createToast, clearToasts } from "mosha-vue-toastify";
export default {
setup() {
const toast = (title, color = "#217dbb") => {
if (title.length > 0) {
createToast(title.charAt(0).toUpperCase() + title.slice(1), {
position: "bottom-right",
hideProgressBar: true,
toastBackgroundColor: color,
timeout: 8000,
});
}
};
const clear = () => {
clearToasts();
Expand Down
18 changes: 9 additions & 9 deletions server/app/admin_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,31 @@ import (

// AdminAnnouncement struct for data needed when admin sends new announcement
type AdminAnnouncement struct {
Subject string `json:"subject" binding:"required"`
Body string `json:"announcement" binding:"required"`
Subject string `json:"subject" validate:"nonzero" binding:"required"`
Body string `json:"announcement" validate:"nonzero" binding:"required"`
}

// EmailUser struct for data needed when admin sends new email to a user
type EmailUser struct {
Subject string `json:"subject" binding:"required"`
Body string `json:"body" binding:"required"`
Subject string `json:"subject" validate:"nonzero" binding:"required"`
Body string `json:"body" validate:"nonzero" binding:"required"`
Email string `json:"email" binding:"required" validate:"mail"`
}

// UpdateMaintenanceInput struct for data needed when user update maintenance
type UpdateMaintenanceInput struct {
ON bool `json:"on" binding:"required"`
ON bool `json:"on" validate:"nonzero" binding:"required"`
}

// SetAdminInput struct for setting users as admins
type SetAdminInput struct {
Email string `json:"email" binding:"required"`
Admin bool `json:"admin" binding:"required"`
Email string `json:"email" binding:"required" validate:"mail"`
Admin bool `json:"admin" validate:"nonzero" binding:"required"`
}

// UpdateNextLaunchInput struct for data needed when updating next launch state
type UpdateNextLaunchInput struct {
Launched bool `json:"launched" binding:"required"`
Launched bool `json:"launched" validate:"nonzero" binding:"required"`
}

// SetPricesInput struct for setting prices as admins
Expand Down Expand Up @@ -556,7 +556,7 @@ func (a *App) CreateNewAnnouncementHandler(req *http.Request) (interface{}, Resp
// @Failure 401 {object} Response
// @Failure 404 {object} Response
// @Failure 500 {object} Response
// @Router /announcement [post]
// @Router /email [post]
func (a *App) SendEmailHandler(req *http.Request) (interface{}, Response) {
var emailUser EmailUser
err := json.NewDecoder(req.Body).Decode(&emailUser)
Expand Down
4 changes: 3 additions & 1 deletion server/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,14 @@ func (a *App) registerHandlers() {
unAuthUserRouter.HandleFunc("/signup/verify_email", WrapFunc(a.VerifySignUpCodeHandler)).Methods("POST", "OPTIONS")
unAuthUserRouter.HandleFunc("/signin", WrapFunc(a.SignInHandler)).Methods("POST", "OPTIONS")
unAuthUserRouter.HandleFunc("/refresh_token", WrapFunc(a.RefreshJWTHandler)).Methods("POST", "OPTIONS")
// TODO: rename it
unAuthUserRouter.HandleFunc("/forgot_password", WrapFunc(a.ForgotPasswordHandler)).Methods("POST", "OPTIONS")
unAuthUserRouter.HandleFunc("/forget_password/verify_email", WrapFunc(a.VerifyForgetPasswordCodeHandler)).Methods("POST", "OPTIONS")

userRouter.HandleFunc("/change_password", WrapFunc(a.ChangePasswordHandler)).Methods("PUT", "OPTIONS")
userRouter.HandleFunc("", WrapFunc(a.UpdateUserHandler)).Methods("PUT", "OPTIONS")
userRouter.HandleFunc("", WrapFunc(a.GetUserHandler)).Methods("GET", "OPTIONS")
userRouter.HandleFunc("", WrapFunc(a.DeleteUserHandler)).Methods("DELETE", "OPTIONS")
userRouter.HandleFunc("/apply_voucher", WrapFunc(a.ApplyForVoucherHandler)).Methods("POST", "OPTIONS")
userRouter.HandleFunc("/activate_voucher", WrapFunc(a.ActivateVoucherHandler)).Methods("PUT", "OPTIONS")
userRouter.HandleFunc("/charge_balance", WrapFunc(a.ChargeBalance)).Methods("PUT", "OPTIONS")
Expand Down Expand Up @@ -196,7 +198,7 @@ func (a *App) registerHandlers() {
voucherRouter.HandleFunc("", WrapFunc(a.ListVouchersHandler)).Methods("GET", "OPTIONS")
voucherRouter.HandleFunc("/{id}", WrapFunc(a.UpdateVoucherHandler)).Methods("PUT", "OPTIONS")
voucherRouter.HandleFunc("", WrapFunc(a.ApproveAllVouchersHandler)).Methods("PUT", "OPTIONS")
voucherRouter.HandleFunc("/reset", WrapFunc(a.ResetUsersVoucherBalanceHandler)).Methods("PUT", "OPTIONS")
voucherRouter.HandleFunc("/all/reset", WrapFunc(a.ResetUsersVoucherBalanceHandler)).Methods("PUT", "OPTIONS")

// middlewares
r.Use(middlewares.LoggingMW)
Expand Down
52 changes: 38 additions & 14 deletions server/app/invoice_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var methods = []method{
}

type PayInvoiceInput struct {
Method method `json:"method" binding:"required"`
Method method `json:"method" validate:"nonzero" binding:"required"`
CardPaymentID string `json:"card_payment_id"`
}

Expand Down Expand Up @@ -231,6 +231,7 @@ func (a *App) PayInvoiceHandler(req *http.Request) (interface{}, Response) {
user.VoucherBalance = 0
}

// TODO: check all update user with 0 value fields
if err = a.db.UpdateUserByID(user); err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
Expand Down Expand Up @@ -305,6 +306,7 @@ func (a *App) PayInvoiceHandler(req *http.Request) (interface{}, Response) {
return nil, BadRequest(fmt.Errorf("invalid payment method, only methods allowed %v", methods))
}

paymentDetails.InvoiceID = id
err = a.db.PayInvoice(id, paymentDetails)
if err == gorm.ErrRecordNotFound {
return nil, NotFound(errors.New("invoice is not found"))
Expand All @@ -316,7 +318,6 @@ func (a *App) PayInvoiceHandler(req *http.Request) (interface{}, Response) {

return ResponseMsg{
Message: "Invoice is paid successfully",
Data: nil,
}, Ok()
}

Expand Down Expand Up @@ -348,6 +349,8 @@ func (a *App) monthlyInvoices() {
log.Error().Err(err).Send()
}

// TODO: what if we want to pay using balances and cards together

// 2. Use balance/voucher balance to pay invoices
user.Balance, user.VoucherBalance, err = a.db.PayUserInvoices(user.ID.String(), user.Balance, user.VoucherBalance)
if err != nil {
Expand Down Expand Up @@ -379,15 +382,14 @@ func (a *App) monthlyInvoices() {
}

func (a *App) createInvoice(userID string, now time.Time) error {
usagePercentageInMonth := deployer.UsagePercentageInMonth(now)
firstDayOfMonth := time.Date(now.Year(), now.Month(), 0, 0, 0, 0, 0, time.Local)
monthStart := time.Date(now.Year(), now.Month(), 0, 0, 0, 0, 0, time.Local)

vms, err := a.db.GetAllVms(userID)
vms, err := a.db.GetAllSuccessfulVms(userID)
if err != nil && err != gorm.ErrRecordNotFound {
return err
}

k8s, err := a.db.GetAllK8s(userID)
k8s, err := a.db.GetAllSuccessfulK8s(userID)
if err != nil && err != gorm.ErrRecordNotFound {
return err
}
Expand All @@ -396,41 +398,63 @@ func (a *App) createInvoice(userID string, now time.Time) error {
var total float64

for _, vm := range vms {
usageStart := monthStart
if vm.CreatedAt.After(monthStart) {
usageStart = vm.CreatedAt
}

usagePercentageInMonth, err := deployer.UsagePercentageInMonth(usageStart, now)
if err != nil {
return err
}

cost := float64(vm.PricePerMonth) * usagePercentageInMonth

items = append(items, models.DeploymentItem{
DeploymentResources: vm.Resources,
DeploymentType: "vm",
DeploymentID: vm.ID,
HasPublicIP: vm.Public,
PeriodInHours: time.Since(firstDayOfMonth).Hours(),
PeriodInHours: time.Since(usageStart).Hours(),
Cost: cost,
})

total += cost
}

for _, cluster := range k8s {
usageStart := monthStart
if cluster.CreatedAt.After(monthStart) {
usageStart = cluster.CreatedAt
}

usagePercentageInMonth, err := deployer.UsagePercentageInMonth(usageStart, now)
if err != nil {
return err
}

cost := float64(cluster.PricePerMonth) * usagePercentageInMonth

items = append(items, models.DeploymentItem{
DeploymentResources: cluster.Master.Resources,
DeploymentType: "k8s",
DeploymentID: cluster.ID,
HasPublicIP: cluster.Master.Public,
PeriodInHours: time.Since(firstDayOfMonth).Hours(),
PeriodInHours: time.Since(usageStart).Hours(),
Cost: cost,
})

total += cost
}

if err = a.db.CreateInvoice(&models.Invoice{
UserID: userID,
Total: total,
Deployments: items,
}); err != nil {
return err
if len(items) > 0 {
if err = a.db.CreateInvoice(&models.Invoice{
UserID: userID,
Total: total,
Deployments: items,
}); err != nil {
return err
}
}

return nil
Expand Down
8 changes: 4 additions & 4 deletions server/app/k8s_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import (
// K8sDeployInput deploy k8s cluster input
type K8sDeployInput struct {
MasterName string `json:"master_name" validate:"min=3,max=20"`
MasterResources string `json:"resources"`
MasterPublic bool `json:"public"`
MasterRegion string `json:"region"`
MasterResources string `json:"resources" validate:"nonzero"`
MasterPublic bool `json:"public" validate:"nonzero"`
MasterRegion string `json:"region" validate:"nonzero"`
Workers []WorkerInput `json:"workers"`
}

// WorkerInput deploy k8s worker input
type WorkerInput struct {
Name string `json:"name" validate:"min=3,max=20"`
Resources string `json:"resources"`
Resources string `json:"resources" validate:"nonzero"`
}

// K8sDeployHandler deploy k8s handler
Expand Down
55 changes: 46 additions & 9 deletions server/app/payments_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ import (
)

type AddCardInput struct {
PaymentMethodID string `json:"payment_method_id" binding:"required"`
CardType string `json:"card_type" binding:"required"`
PaymentMethodID string `json:"payment_method_id" binding:"required" validate:"nonzero"`
CardType string `json:"card_type" binding:"required" validate:"nonzero"`
}

type SetDefaultCardInput struct {
PaymentMethodID string `json:"payment_method_id" binding:"required"`
PaymentMethodID string `json:"payment_method_id" binding:"required" validate:"nonzero"`
}

type ChargeBalance struct {
PaymentMethodID string `json:"payment_method_id" binding:"required"`
Amount float64 `json:"amount" binding:"required"`
PaymentMethodID string `json:"payment_method_id" binding:"required" validate:"nonzero"`
Amount float64 `json:"amount" binding:"required" validate:"nonzero"`
}

// Example endpoint: Add a new card
Expand Down Expand Up @@ -101,7 +101,6 @@ func (a *App) AddCardHandler(req *http.Request) (interface{}, Response) {
}

if !unique {
log.Error().Err(err).Send()
return nil, BadRequest(errors.New("card is added before"))
}

Expand Down Expand Up @@ -132,7 +131,7 @@ func (a *App) AddCardHandler(req *http.Request) (interface{}, Response) {
// if no payment is added before then we update the user payment ID with it as a default
if len(strings.TrimSpace(user.StripeDefaultPaymentID)) == 0 {
// Update the default payment method for future payments
err = updateDefaultPaymentMethod(user.StripeCustomerID, input.PaymentMethodID)
err = updateDefaultPaymentMethod(user.StripeCustomerID, paymentMethod.ID)
if err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
Expand Down Expand Up @@ -278,6 +277,15 @@ func (a *App) ListCardHandler(req *http.Request) (interface{}, Response) {
func (a *App) DeleteCardHandler(req *http.Request) (interface{}, Response) {
userID := req.Context().Value(middlewares.UserIDKey("UserID")).(string)

user, err := a.db.GetUserByID(userID)
if err == gorm.ErrRecordNotFound {
return nil, NotFound(errors.New("user is not found"))
}
if err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}

id, err := strconv.Atoi(mux.Vars(req)["id"])
if err != nil {
log.Error().Err(err).Send()
Expand Down Expand Up @@ -315,13 +323,13 @@ func (a *App) DeleteCardHandler(req *http.Request) (interface{}, Response) {
var vms []models.VM
var k8s []models.K8sCluster
if len(cards) == 1 {
vms, err = a.db.GetAllVms(userID)
vms, err = a.db.GetAllSuccessfulVms(userID)
if err != nil && err != gorm.ErrRecordNotFound {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}

k8s, err = a.db.GetAllK8s(userID)
k8s, err = a.db.GetAllSuccessfulK8s(userID)
if err != nil && err != gorm.ErrRecordNotFound {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
Expand All @@ -332,6 +340,35 @@ func (a *App) DeleteCardHandler(req *http.Request) (interface{}, Response) {
return nil, BadRequest(errors.New("you have active deployment and cannot delete the card"))
}

// Update the default payment method for future payments (if deleted card is the default)
if card.PaymentMethodID == user.StripeDefaultPaymentID {
var newPaymentMethod string
// no more cards
if len(cards) == 1 {
newPaymentMethod = ""
}

for _, c := range cards {
if c.PaymentMethodID != user.StripeDefaultPaymentID {
newPaymentMethod = c.PaymentMethodID
if err = updateDefaultPaymentMethod(card.CustomerID, c.PaymentMethodID); err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}
break
}
}

err = a.db.UpdateUserPaymentMethod(userID, newPaymentMethod)
if err == gorm.ErrRecordNotFound {
return nil, NotFound(errors.New("user is not found"))
}
if err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}
}

// If user has another cards or no active deployments, so can delete
err = detachPaymentMethod(card.PaymentMethodID)
if err != nil {
Expand Down
Loading

0 comments on commit 8bde0ec

Please sign in to comment.