Files
worker/app/models/account.go
Gleb Tv 2c7a0236da feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
2026-07-13 17:55:14 +03:00

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(&notification).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(&notification).Association("Contacts").Append(&contact); err != nil {
return errors.Wrap(err, "failed to add contact to notification")
}
}
if err := tx.Model(&notification).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
}