118 строки
4.8 KiB
Go
118 строки
4.8 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
|
|
"rsgit.ru/rsmon/rsmon/app/models/concerns"
|
|
)
|
|
|
|
// Account represents a tenant — an isolated billing/permissions boundary
|
|
// that groups users, contacts, groups, monitors and notifications.
|
|
//
|
|
// Access to an Account is granted via the Access join table (see Access).
|
|
// `Role` on Account is a gorm:"-":all" virtual column populated by the
|
|
// controllers for the current session — it is the role the calling user
|
|
// holds on THIS account, not a property of the account itself.
|
|
type Account struct {
|
|
concerns.Model
|
|
|
|
Name string `json:"name"`
|
|
Accesses []Access `json:"-"`
|
|
Contacts []Contact `json:"-"`
|
|
Groups []Group `json:"-"`
|
|
Notifications []Notification `json:"-"`
|
|
PlanID *int64 `gorm:"type:bigint REFERENCES plans(id)" json:"-"`
|
|
Plan *Plan `json:"plan"`
|
|
// Diagnostic overrides are available only to plans that include confirmations.
|
|
// Nil keeps the catalog value; bounds are enforced by DiagnosticSettings.
|
|
ConfirmTimeoutSec *int `json:"confirm_timeout_sec,omitempty"`
|
|
HealthWindowSec *int `json:"health_window_sec,omitempty"`
|
|
HealthRateThreshold *float64 `json:"health_rate_threshold,omitempty"`
|
|
HealthMinAttempts *int `json:"health_min_attempts,omitempty"`
|
|
Role string `gorm:"-:all" json:"role"`
|
|
Timezone string `json:"timezone"`
|
|
Language string `gorm:"default:'ru'" json:"language"`
|
|
Disabled bool `gorm:"not null;default:false" json:"disabled"`
|
|
Blocked bool `gorm:"not null;default:false" json:"blocked"`
|
|
PaidUntil *time.Time `json:"paid_until"`
|
|
TrialEndsAt *time.Time `json:"trial_ends_at,omitempty"`
|
|
Deleted bool `gorm:"not null;default:false"`
|
|
|
|
concerns.Timestamped
|
|
Audited
|
|
}
|
|
|
|
// Users provides functionality.
|
|
func (a Account) Users() []User { //nolint:gocritic // hugeParam: accepted for interface compatibility
|
|
users := make([]User, 0)
|
|
err := DB().Where("id IN (SELECT user_id FROM accesses WHERE account_id = ?)", a.ID).Find(&users).Error
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return users
|
|
}
|
|
|
|
// CreateAccountForUser provides functionality.
|
|
func CreateAccountForUser(name string, u *User) (*Account, error) {
|
|
trialPlan := Plan{}
|
|
if err := DB().Where("code = ? AND archived = FALSE", "team").First(&trialPlan).Error; err != nil {
|
|
return nil, errors.Wrap(err, "failed to find trial plan")
|
|
}
|
|
trialEndsAt := time.Now().UTC().AddDate(0, 0, 14)
|
|
account := Account{PlanID: &trialPlan.ID, TrialEndsAt: &trialEndsAt}
|
|
if name != "" {
|
|
account.Name = name
|
|
}
|
|
err := DB().Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Create(&account).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create account")
|
|
}
|
|
if err := tx.Create(&Subscription{
|
|
AccountID: account.ID, PlanID: trialPlan.ID, Provider: "manual", Status: SubscriptionStatusTrialing,
|
|
BillingCycle: "monthly", Currency: trialPlan.Currency, AmountMinor: trialPlan.PriceMonthlyMinor, CurrentPeriodEnd: &trialEndsAt, TrialEndsAt: &trialEndsAt,
|
|
}).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create subscription")
|
|
}
|
|
var subscription Subscription
|
|
if err := tx.Where("account_id = ?", account.ID).First(&subscription).Error; err != nil {
|
|
return errors.Wrap(err, "failed to load trial subscription")
|
|
}
|
|
if err := tx.Create(&SubscriptionEvent{SubscriptionID: subscription.ID, AccountID: account.ID, Provider: "manual", Kind: "trial_started", ToPlanID: &trialPlan.ID, ActorUserID: &u.ID, CreatedAt: time.Now().UTC()}).Error; err != nil {
|
|
return errors.Wrap(err, "failed to record trial")
|
|
}
|
|
access := Access{AccountID: account.ID, UserID: &u.ID, Role: "owner", SeatType: "admin"}
|
|
if err := tx.Create(&access).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create access")
|
|
}
|
|
group := Group{AccountID: account.ID, Name: "Основные"}
|
|
if err := tx.Create(&group).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create group")
|
|
}
|
|
notification := Notification{AccountID: account.ID, Name: "Основные", Enabled: true}
|
|
if err := tx.Create(¬ification).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create notification")
|
|
}
|
|
if u.Email != nil {
|
|
contact := Contact{AccountID: &account.ID, UserID: &u.ID, Kind: "email", Value: *u.Email}
|
|
if err := tx.Create(&contact).Error; err != nil {
|
|
return errors.Wrap(err, "failed to create contact")
|
|
}
|
|
if err := tx.Model(¬ification).Association("Contacts").Append(&contact); err != nil {
|
|
return errors.Wrap(err, "failed to add contact to notification")
|
|
}
|
|
}
|
|
if err := tx.Model(¬ification).Association("Groups").Append(&group); err != nil {
|
|
return errors.Wrap(err, "failed to add group to notification")
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &account, nil
|
|
}
|