feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
Этот коммит содержится в:
117
app/models/account.go
Обычный файл
117
app/models/account.go
Обычный файл
@@ -0,0 +1,117 @@
|
||||
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
|
||||
}
|
||||
Ссылка в новой задаче
Block a user