satellite/{console,satellitedb}: add account freeze service

This change adds an account freeze service with methods for checking
if a user is frozen, freezing a user, and unfreezing a user.
Furthermore, methods for altering the usage limits of a user or project
have been implemented for use by the account freeze service.

Change-Id: I77fecfac5c152f134bec90165acfe4f1dea957e7
This commit is contained in:
Jeremy Wharton 2022-12-15 01:11:03 -06:00 committed by Storj Robot
parent 7b851b42f7
commit 471f9e4e10
12 changed files with 477 additions and 67 deletions

View File

@ -5,21 +5,26 @@ package console
import (
"context"
"database/sql"
"errors"
"time"
"github.com/zeebo/errs"
"storj.io/common/uuid"
)
// ErrAccountFreeze is the class for errors that occur during operation of the account freeze service.
var ErrAccountFreeze = errs.Class("account freeze service")
// AccountFreezeEvents exposes methods to manage the account freeze events table in database.
//
// architecture: Database
type AccountFreezeEvents interface {
// Insert is a method for inserting account freeze event into the database.
Insert(ctx context.Context, event *AccountFreezeEvent) (*AccountFreezeEvent, error)
// Upsert is a method for updating an account freeze event if it exists and inserting it otherwise.
Upsert(ctx context.Context, event *AccountFreezeEvent) (*AccountFreezeEvent, error)
// Get is a method for querying account freeze event from the database by user ID and event type.
Get(ctx context.Context, userID uuid.UUID, eventType AccountFreezeEventType) (*AccountFreezeEvent, error)
// UpdateLimits is a method for updating the limits of an account freeze event by user ID and event type.
UpdateLimits(ctx context.Context, userID uuid.UUID, eventType AccountFreezeEventType, limits *AccountFreezeEventLimits) error
// DeleteAllByUserID is a method for deleting all account freeze events from the database by user ID.
DeleteAllByUserID(ctx context.Context, userID uuid.UUID) error
}
@ -47,3 +52,140 @@ const (
// Warning signifies that the user has been warned that they may be frozen soon.
Warning AccountFreezeEventType = 1
)
// AccountFreezeService encapsulates operations concerning account freezes.
type AccountFreezeService struct {
freezeEventsDB AccountFreezeEvents
usersDB Users
projectsDB Projects
}
// NewAccountFreezeService creates a new account freeze service.
func NewAccountFreezeService(freezeEventsDB AccountFreezeEvents, usersDB Users, projectsDB Projects) *AccountFreezeService {
return &AccountFreezeService{
freezeEventsDB: freezeEventsDB,
usersDB: usersDB,
projectsDB: projectsDB,
}
}
// IsUserFrozen returns whether the user specified by the given ID is frozen.
func (s *AccountFreezeService) IsUserFrozen(ctx context.Context, userID uuid.UUID) (_ bool, err error) {
defer mon.Task()(&ctx)(&err)
_, err = s.freezeEventsDB.Get(ctx, userID, Freeze)
switch {
case errors.Is(err, sql.ErrNoRows):
return false, nil
case err != nil:
return false, ErrAccountFreeze.Wrap(err)
default:
return true, nil
}
}
// FreezeUser freezes the user specified by the given ID.
func (s *AccountFreezeService) FreezeUser(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)
user, err := s.usersDB.Get(ctx, userID)
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
event, err := s.freezeEventsDB.Get(ctx, userID, Freeze)
if errors.Is(err, sql.ErrNoRows) {
event = &AccountFreezeEvent{
UserID: userID,
Type: Freeze,
Limits: &AccountFreezeEventLimits{
User: UsageLimits{
Storage: user.ProjectStorageLimit,
Bandwidth: user.ProjectBandwidthLimit,
Segment: user.ProjectSegmentLimit,
},
Projects: make(map[uuid.UUID]UsageLimits),
},
}
} else if err != nil {
return ErrAccountFreeze.Wrap(err)
}
userLimits := UsageLimits{
Storage: user.ProjectStorageLimit,
Bandwidth: user.ProjectBandwidthLimit,
Segment: user.ProjectSegmentLimit,
}
// If user limits have been zeroed already, we should not override what is in the freeze table.
if userLimits != (UsageLimits{}) {
event.Limits.User = userLimits
}
projects, err := s.projectsDB.GetOwn(ctx, userID)
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
for _, p := range projects {
projLimits := UsageLimits{}
if p.StorageLimit != nil {
projLimits.Storage = p.StorageLimit.Int64()
}
if p.BandwidthLimit != nil {
projLimits.Bandwidth = p.BandwidthLimit.Int64()
}
if p.SegmentLimit != nil {
projLimits.Segment = *p.SegmentLimit
}
// If project limits have been zeroed already, we should not override what is in the freeze table.
if projLimits != (UsageLimits{}) {
event.Limits.Projects[p.ID] = projLimits
}
}
_, err = s.freezeEventsDB.Upsert(ctx, event)
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
err = s.usersDB.UpdateUserProjectLimits(ctx, userID, UsageLimits{})
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
for _, proj := range projects {
err := s.projectsDB.UpdateUsageLimits(ctx, proj.ID, UsageLimits{})
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
}
return nil
}
// UnfreezeUser reverses the freeze placed on the user specified by the given ID.
func (s *AccountFreezeService) UnfreezeUser(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)
event, err := s.freezeEventsDB.Get(ctx, userID, Freeze)
if errors.Is(err, sql.ErrNoRows) {
return ErrAccountFreeze.New("user is not frozen")
}
if event.Limits == nil {
return ErrAccountFreeze.New("freeze event limits are nil")
}
for id, limits := range event.Limits.Projects {
err := s.projectsDB.UpdateUsageLimits(ctx, id, limits)
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
}
err = s.usersDB.UpdateUserProjectLimits(ctx, userID, event.Limits.User)
if err != nil {
return ErrAccountFreeze.Wrap(err)
}
return ErrAccountFreeze.Wrap(s.freezeEventsDB.DeleteAllByUserID(ctx, userID))
}

View File

@ -0,0 +1,210 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package console_test
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite/console"
)
func getUserLimits(u *console.User) console.UsageLimits {
return console.UsageLimits{
Storage: u.ProjectStorageLimit,
Bandwidth: u.ProjectBandwidthLimit,
Segment: u.ProjectSegmentLimit,
}
}
func getProjectLimits(p *console.Project) console.UsageLimits {
return console.UsageLimits{
Storage: p.StorageLimit.Int64(),
Bandwidth: p.BandwidthLimit.Int64(),
Segment: *p.SegmentLimit,
}
}
func randUsageLimits() console.UsageLimits {
return console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
}
func TestAccountFreeze(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
usersDB := sat.DB.Console().Users()
projectsDB := sat.DB.Console().Projects()
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
userLimits := randUsageLimits()
user, err := sat.AddUser(ctx, console.CreateUser{
FullName: "Test User",
Email: "user@mail.test",
}, 2)
require.NoError(t, err)
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
projLimits := randUsageLimits()
proj, err := sat.AddProject(ctx, user.ID, "")
require.NoError(t, err)
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj.ID, projLimits))
frozen, err := service.IsUserFrozen(ctx, user.ID)
require.NoError(t, err)
require.False(t, frozen)
require.NoError(t, service.FreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Zero(t, getUserLimits(user))
proj, err = projectsDB.Get(ctx, proj.ID)
require.NoError(t, err)
require.Zero(t, getProjectLimits(proj))
frozen, err = service.IsUserFrozen(ctx, user.ID)
require.NoError(t, err)
require.True(t, frozen)
})
}
func TestAccountUnfreeze(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
usersDB := sat.DB.Console().Users()
projectsDB := sat.DB.Console().Projects()
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
userLimits := randUsageLimits()
user, err := sat.AddUser(ctx, console.CreateUser{
FullName: "Test User",
Email: "user@mail.test",
}, 2)
require.NoError(t, err)
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
projLimits := randUsageLimits()
proj, err := sat.AddProject(ctx, user.ID, "")
require.NoError(t, err)
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj.ID, projLimits))
require.NoError(t, service.FreezeUser(ctx, user.ID))
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, userLimits, getUserLimits(user))
proj, err = projectsDB.Get(ctx, proj.ID)
require.NoError(t, err)
require.Equal(t, projLimits, getProjectLimits(proj))
frozen, err := service.IsUserFrozen(ctx, user.ID)
require.NoError(t, err)
require.False(t, frozen)
})
}
func TestAccountFreezeAlreadyFrozen(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
usersDB := sat.DB.Console().Users()
projectsDB := sat.DB.Console().Projects()
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
userLimits := randUsageLimits()
user, err := sat.AddUser(ctx, console.CreateUser{
FullName: "Test User",
Email: "user@mail.test",
}, 2)
require.NoError(t, err)
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
proj1Limits := randUsageLimits()
proj1, err := sat.AddProject(ctx, user.ID, "")
require.NoError(t, err)
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj1.ID, proj1Limits))
// Freezing a frozen user should freeze any projects that were unable to be frozen prior.
// The limits stored for projects frozen by the prior freeze should not be modified.
t.Run("Project limits", func(t *testing.T) {
require.NoError(t, service.FreezeUser(ctx, user.ID))
proj2Limits := randUsageLimits()
proj2, err := sat.AddProject(ctx, user.ID, "")
require.NoError(t, err)
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj2.ID, proj2Limits))
require.NoError(t, service.FreezeUser(ctx, user.ID))
user, err := usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Zero(t, getUserLimits(user))
proj2, err = projectsDB.Get(ctx, proj2.ID)
require.NoError(t, err)
require.Zero(t, getProjectLimits(proj2))
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, userLimits, getUserLimits(user))
proj1, err = projectsDB.Get(ctx, proj1.ID)
require.NoError(t, err)
require.Equal(t, proj1Limits, getProjectLimits(proj1))
proj2, err = projectsDB.Get(ctx, proj2.ID)
require.NoError(t, err)
require.Equal(t, proj2Limits, getProjectLimits(proj2))
})
// Freezing a frozen user should freeze the user's limits if they were unable to be frozen prior.
t.Run("Unfrozen user limits", func(t *testing.T) {
user, err := usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.NoError(t, service.FreezeUser(ctx, user.ID))
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
require.NoError(t, service.FreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Zero(t, getUserLimits(user))
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, userLimits, getUserLimits(user))
})
// Freezing a frozen user should not modify user limits stored by the prior freeze.
t.Run("Frozen user limits", func(t *testing.T) {
require.NoError(t, service.FreezeUser(ctx, user.ID))
require.NoError(t, service.FreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Zero(t, getUserLimits(user))
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
user, err = usersDB.Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, userLimits, getUserLimits(user))
})
})
}

View File

@ -51,6 +51,9 @@ type Projects interface {
GetMaxBuckets(ctx context.Context, id uuid.UUID) (*int, error)
// UpdateBucketLimit is a method for updating projects bucket limit.
UpdateBucketLimit(ctx context.Context, id uuid.UUID, newLimit int) error
// UpdateUsageLimits is a method for updating project's usage limits.
UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
}
// UsageLimitsConfig is a configuration struct for default per-project usage limits.

View File

@ -39,6 +39,8 @@ type Users interface {
Update(ctx context.Context, userID uuid.UUID, request UpdateUserRequest) error
// UpdatePaidTier sets whether the user is in the paid tier.
UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier bool, projectBandwidthLimit, projectStorageLimit memory.Size, projectSegmentLimit int64, projectLimit int) error
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
// GetProjectLimit is a method to get the users project limit
GetProjectLimit(ctx context.Context, id uuid.UUID) (limit int, err error)
// GetUserProjectLimits is a method to get the users storage and bandwidth limits for new projects.

View File

@ -20,8 +20,8 @@ type accountFreezeEvents struct {
db dbx.Methods
}
// Insert is a method for inserting account freeze event into the database.
func (events *accountFreezeEvents) Insert(ctx context.Context, event *console.AccountFreezeEvent) (_ *console.AccountFreezeEvent, err error) {
// Upsert is a method for updating an account freeze event if it exists and inserting it otherwise.
func (events *accountFreezeEvents) Upsert(ctx context.Context, event *console.AccountFreezeEvent) (_ *console.AccountFreezeEvent, err error) {
defer mon.Task()(&ctx)(&err)
if event == nil {
@ -37,7 +37,7 @@ func (events *accountFreezeEvents) Insert(ctx context.Context, event *console.Ac
createFields.Limits = dbx.AccountFreezeEvent_Limits(limitBytes)
}
dbxEvent, err := events.db.Create_AccountFreezeEvent(ctx,
dbxEvent, err := events.db.Replace_AccountFreezeEvent(ctx,
dbx.AccountFreezeEvent_UserId(event.UserID.Bytes()),
dbx.AccountFreezeEvent_Event(int(event.Type)),
createFields,
@ -64,26 +64,6 @@ func (events *accountFreezeEvents) Get(ctx context.Context, userID uuid.UUID, ev
return fromDBXAccountFreezeEvent(dbxEvent)
}
// UpdateLimits is a method for updating the limits of an account freeze event by user ID and event type.
func (events *accountFreezeEvents) UpdateLimits(ctx context.Context, userID uuid.UUID, eventType console.AccountFreezeEventType, limits *console.AccountFreezeEventLimits) (err error) {
defer mon.Task()(&ctx)(&err)
limitBytes, err := json.Marshal(limits)
if err != nil {
return err
}
_, err = events.db.Update_AccountFreezeEvent_By_UserId_And_Event(ctx,
dbx.AccountFreezeEvent_UserId(userID.Bytes()),
dbx.AccountFreezeEvent_Event(int(eventType)),
dbx.AccountFreezeEvent_Update_Fields{
Limits: dbx.AccountFreezeEvent_Limits(limitBytes),
},
)
return err
}
// DeleteAllByUserID is a method for deleting all account freeze events from the database by user ID.
func (events *accountFreezeEvents) DeleteAllByUserID(ctx context.Context, userID uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)

View File

@ -22,11 +22,7 @@ import (
func TestAccountFreezeEvents(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
randUsageLimits := func() console.UsageLimits {
return console.UsageLimits{
Storage: rand.Int63(),
Bandwidth: rand.Int63(),
Segment: rand.Int63(),
}
return console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
}
event := &console.AccountFreezeEvent{
@ -44,12 +40,12 @@ func TestAccountFreezeEvents(t *testing.T) {
eventsDB := db.Console().AccountFreezeEvents()
t.Run("Can't insert nil event", func(t *testing.T) {
_, err := eventsDB.Insert(ctx, nil)
_, err := eventsDB.Upsert(ctx, nil)
require.Error(t, err)
})
t.Run("Insert event", func(t *testing.T) {
dbEvent, err := eventsDB.Insert(ctx, event)
dbEvent, err := eventsDB.Upsert(ctx, event)
require.NoError(t, err)
require.NotNil(t, dbEvent)
require.WithinDuration(t, time.Now(), dbEvent.CreatedAt, time.Minute)
@ -57,11 +53,6 @@ func TestAccountFreezeEvents(t *testing.T) {
require.Equal(t, event, dbEvent)
})
t.Run("Can't insert duplicate event", func(t *testing.T) {
_, err := eventsDB.Insert(ctx, event)
require.Error(t, err)
})
t.Run("Get event", func(t *testing.T) {
dbEvent, err := eventsDB.Get(ctx, event.UserID, event.Type)
require.NoError(t, err)
@ -71,18 +62,22 @@ func TestAccountFreezeEvents(t *testing.T) {
})
t.Run("Update event limits", func(t *testing.T) {
limits := &console.AccountFreezeEventLimits{
event.Limits = &console.AccountFreezeEventLimits{
User: randUsageLimits(),
Projects: map[uuid.UUID]console.UsageLimits{
testrand.UUID(): randUsageLimits(),
},
}
require.NoError(t, eventsDB.UpdateLimits(ctx, event.UserID, event.Type, limits))
_, err := eventsDB.Upsert(ctx, event)
require.NoError(t, err)
dbEvent, err := eventsDB.Get(ctx, event.UserID, event.Type)
require.NoError(t, err)
require.Equal(t, limits, dbEvent.Limits)
require.Equal(t, event.Limits, dbEvent.Limits)
require.NoError(t, eventsDB.UpdateLimits(ctx, event.UserID, event.Type, nil))
event.Limits = nil
_, err = eventsDB.Upsert(ctx, event)
require.NoError(t, err)
dbEvent, err = eventsDB.Get(ctx, event.UserID, event.Type)
require.NoError(t, err)
require.Nil(t, dbEvent.Limits)

View File

@ -10,7 +10,7 @@ model account_freeze_event (
field created_at timestamp ( default current_timestamp )
)
create account_freeze_event()
create account_freeze_event( replace )
read one (
select account_freeze_event

View File

@ -12830,7 +12830,7 @@ type WalletAddress_Row struct {
WalletAddress []byte
}
func (obj *pgxImpl) Create_AccountFreezeEvent(ctx context.Context,
func (obj *pgxImpl) Replace_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
@ -12844,7 +12844,7 @@ func (obj *pgxImpl) Create_AccountFreezeEvent(ctx context.Context,
var __placeholders = &__sqlbundle_Hole{SQL: __sqlbundle_Literal("?, ?, ?")}
var __clause = &__sqlbundle_Hole{SQL: __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("("), __columns, __sqlbundle_Literal(") VALUES ("), __placeholders, __sqlbundle_Literal(")")}}}
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" ON CONFLICT ( user_id, event ) DO UPDATE SET user_id = EXCLUDED.user_id, event = EXCLUDED.event, limits = EXCLUDED.limits, created_at = EXCLUDED.created_at RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
var __values []interface{}
__values = append(__values, __user_id_val, __event_val, __limits_val)
@ -20781,7 +20781,7 @@ func (obj *pgxImpl) deleteAll(ctx context.Context) (count int64, err error) {
}
func (obj *pgxcockroachImpl) Create_AccountFreezeEvent(ctx context.Context,
func (obj *pgxcockroachImpl) Replace_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
@ -20795,7 +20795,7 @@ func (obj *pgxcockroachImpl) Create_AccountFreezeEvent(ctx context.Context,
var __placeholders = &__sqlbundle_Hole{SQL: __sqlbundle_Literal("?, ?, ?")}
var __clause = &__sqlbundle_Hole{SQL: __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("("), __columns, __sqlbundle_Literal(") VALUES ("), __placeholders, __sqlbundle_Literal(")")}}}
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
var __values []interface{}
__values = append(__values, __user_id_val, __event_val, __limits_val)
@ -29171,19 +29171,6 @@ func (rx *Rx) CreateNoReturn_StorjscanWallet(ctx context.Context,
}
func (rx *Rx) Create_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
account_freeze_event *AccountFreezeEvent, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Create_AccountFreezeEvent(ctx, account_freeze_event_user_id, account_freeze_event_event, optional)
}
func (rx *Rx) Create_ApiKey(ctx context.Context,
api_key_id ApiKey_Id_Field,
api_key_project_id ApiKey_ProjectId_Field,
@ -30515,6 +30502,19 @@ func (rx *Rx) ReplaceNoReturn_StoragenodePaystub(ctx context.Context,
}
func (rx *Rx) Replace_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
account_freeze_event *AccountFreezeEvent, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Replace_AccountFreezeEvent(ctx, account_freeze_event_user_id, account_freeze_event_event, optional)
}
func (rx *Rx) UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
accounting_timestamps_name AccountingTimestamps_Name_Field,
update AccountingTimestamps_Update_Fields) (
@ -30989,12 +30989,6 @@ type Methods interface {
storjscan_wallet_wallet_address StorjscanWallet_WalletAddress_Field) (
err error)
Create_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
account_freeze_event *AccountFreezeEvent, err error)
Create_ApiKey(ctx context.Context,
api_key_id ApiKey_Id_Field,
api_key_project_id ApiKey_ProjectId_Field,
@ -31612,6 +31606,12 @@ type Methods interface {
storagenode_paystub_distributed StoragenodePaystub_Distributed_Field) (
err error)
Replace_AccountFreezeEvent(ctx context.Context,
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
account_freeze_event_event AccountFreezeEvent_Event_Field,
optional AccountFreezeEvent_Create_Fields) (
account_freeze_event *AccountFreezeEvent, err error)
UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
accounting_timestamps_name AccountingTimestamps_Name_Field,
update AccountingTimestamps_Update_Fields) (

View File

@ -455,3 +455,18 @@ func (projects *projects) GetMaxBuckets(ctx context.Context, id uuid.UUID) (maxB
}
return dbxRow.MaxBuckets, nil
}
// UpdateUsageLimits is a method for updating project's bandwidth, storage, and segment limits.
func (projects *projects) UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = projects.db.Update_Project_By_Id(ctx,
dbx.Project_Id(id[:]),
dbx.Project_Update_Fields{
BandwidthLimit: dbx.Project_BandwidthLimit(limits.Bandwidth),
UsageLimit: dbx.Project_UsageLimit(limits.Storage),
SegmentLimit: dbx.Project_SegmentLimit(limits.Segment),
},
)
return err
}

View File

@ -4,6 +4,7 @@
package satellitedb_test
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
@ -54,3 +55,23 @@ func TestProjectsGetSalt(t *testing.T) {
require.NoError(t, err)
})
}
func TestUpdateProjectUsageLimits(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
limits := console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
projectsRepo := db.Console().Projects()
proj, err := projectsRepo.Insert(ctx, &console.Project{})
require.NoError(t, err)
require.NotNil(t, proj)
err = projectsRepo.UpdateUsageLimits(ctx, proj.ID, limits)
require.NoError(t, err)
proj, err = projectsRepo.Get(ctx, proj.ID)
require.NoError(t, err)
require.Equal(t, limits.Bandwidth, proj.BandwidthLimit.Int64())
require.Equal(t, limits.Storage, proj.StorageLimit.Int64())
require.Equal(t, limits.Segment, *proj.SegmentLimit)
})
}

View File

@ -240,6 +240,23 @@ func (users *users) UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier b
return err
}
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
func (users *users) UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = users.db.Update_User_By_Id(
ctx,
dbx.User_Id(id[:]),
dbx.User_Update_Fields{
ProjectBandwidthLimit: dbx.User_ProjectBandwidthLimit(limits.Bandwidth),
ProjectStorageLimit: dbx.User_ProjectStorageLimit(limits.Storage),
ProjectSegmentLimit: dbx.User_ProjectSegmentLimit(limits.Segment),
},
)
return err
}
// GetProjectLimit is a method to get the users project limit.
func (users *users) GetProjectLimit(ctx context.Context, id uuid.UUID) (limit int, err error) {
defer mon.Task()(&ctx)(&err)

View File

@ -4,6 +4,7 @@
package satellitedb_test
import (
"math/rand"
"testing"
"time"
@ -301,3 +302,27 @@ func TestUpdateUser(t *testing.T) {
require.Equal(t, u, updatedUser)
})
}
func TestUpdateUserProjectLimits(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
limits := console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
usersRepo := db.Console().Users()
user, err := usersRepo.Insert(ctx, &console.User{
ID: testrand.UUID(),
FullName: "User",
Email: "test@mail.test",
PasswordHash: []byte("123a123"),
})
require.NoError(t, err)
err = usersRepo.UpdateUserProjectLimits(ctx, user.ID, limits)
require.NoError(t, err)
user, err = usersRepo.Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, limits.Bandwidth, user.ProjectBandwidthLimit)
require.Equal(t, limits.Storage, user.ProjectStorageLimit)
require.Equal(t, limits.Segment, user.ProjectSegmentLimit)
})
}