satellite/admin: list users pending deletion
This change adds an endpoint to the admin API and UI to get a list of users pending deletion and have no unpaid invoice. Issue: #6410 Change-Id: I906dbf9eee9e7469e45f0c622a891867bf0cc201
This commit is contained in:
parent
405491e8d0
commit
513c3cc632
@ -24,6 +24,7 @@ Requires setting `Authorization` header for requests.
|
|||||||
* [PUT /api/users/{user-email}/violation-freeze](#put-apiusersuser-emailviolation-freeze)
|
* [PUT /api/users/{user-email}/violation-freeze](#put-apiusersuser-emailviolation-freeze)
|
||||||
* [DELETE /api/users/{user-email}/violation-freeze](#delete-apiusersuser-emailviolation-freeze)
|
* [DELETE /api/users/{user-email}/violation-freeze](#delete-apiusersuser-emailviolation-freeze)
|
||||||
* [DELETE /api/users/{user-email}/billing-warning](#delete-apiusersuser-emailbilling-warning)
|
* [DELETE /api/users/{user-email}/billing-warning](#delete-apiusersuser-emailbilling-warning)
|
||||||
|
* [GET /api/users/pending-deletion](#get-apiuserspending-deletion)
|
||||||
* [PATCH /api/users/{user-email}/geofence](#patch-apiusersuser-emailgeofence)
|
* [PATCH /api/users/{user-email}/geofence](#patch-apiusersuser-emailgeofence)
|
||||||
* [DELETE /api/users/{user-email}/geofence](#delete-apiusersuser-emailgeofence)
|
* [DELETE /api/users/{user-email}/geofence](#delete-apiusersuser-emailgeofence)
|
||||||
* [OAuth Client Management](#oauth-client-management)
|
* [OAuth Client Management](#oauth-client-management)
|
||||||
@ -199,6 +200,12 @@ User status is set back to Active. This is the only way to exit the violation fr
|
|||||||
|
|
||||||
Removes the billing warning status from a user's account.
|
Removes the billing warning status from a user's account.
|
||||||
|
|
||||||
|
#### GET /api/users/pending-deletion
|
||||||
|
|
||||||
|
Returns a limited list of users pending deletion and have no unpaid invoices.
|
||||||
|
Required parameters: `limit` and `page`.
|
||||||
|
Example: `/api/users/pending-deletion?limit=10&page=1`
|
||||||
|
|
||||||
#### PATCH /api/users/{user-email}/geofence
|
#### PATCH /api/users/{user-email}/geofence
|
||||||
|
|
||||||
Sets the account level geofence for the user.
|
Sets the account level geofence for the user.
|
||||||
@ -454,7 +461,7 @@ A successful response body:
|
|||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"id": "12345678-1234-1234-1234-123456789abc",
|
"id": "12345678-1234-1234-1234-123456789abc",
|
||||||
"name": "My Project",
|
"name": "My Project"
|
||||||
},
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
"id": "12345678-1234-1234-1234-123456789abc",
|
"id": "12345678-1234-1234-1234-123456789abc",
|
||||||
|
@ -174,6 +174,7 @@ func NewServer(
|
|||||||
limitUpdateAPI.HandleFunc("/users/{useremail}/billing-warning", server.billingUnWarnUser).Methods("DELETE")
|
limitUpdateAPI.HandleFunc("/users/{useremail}/billing-warning", server.billingUnWarnUser).Methods("DELETE")
|
||||||
limitUpdateAPI.HandleFunc("/users/{useremail}/violation-freeze", server.violationFreezeUser).Methods("PUT")
|
limitUpdateAPI.HandleFunc("/users/{useremail}/violation-freeze", server.violationFreezeUser).Methods("PUT")
|
||||||
limitUpdateAPI.HandleFunc("/users/{useremail}/violation-freeze", server.violationUnfreezeUser).Methods("DELETE")
|
limitUpdateAPI.HandleFunc("/users/{useremail}/violation-freeze", server.violationUnfreezeUser).Methods("DELETE")
|
||||||
|
limitUpdateAPI.HandleFunc("/users/pending-deletion", server.usersPendingDeletion).Methods("GET")
|
||||||
limitUpdateAPI.HandleFunc("/projects/{project}/limit", server.getProjectLimit).Methods("GET")
|
limitUpdateAPI.HandleFunc("/projects/{project}/limit", server.getProjectLimit).Methods("GET")
|
||||||
limitUpdateAPI.HandleFunc("/projects/{project}/limit", server.putProjectLimit).Methods("PUT", "POST")
|
limitUpdateAPI.HandleFunc("/projects/{project}/limit", server.putProjectLimit).Methods("PUT", "POST")
|
||||||
|
|
||||||
|
@ -343,6 +343,17 @@ export class Admin {
|
|||||||
return this.fetch('GET', `users/${email}`);
|
return this.fetch('GET', `users/${email}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'get users pending deletion',
|
||||||
|
desc: 'Get the information of a users pending deletion and have no unpaid invoices',
|
||||||
|
params: [
|
||||||
|
['Limit', new InputText('number', true)],
|
||||||
|
['Page', new InputText('number', true)]
|
||||||
|
],
|
||||||
|
func: async (limit: number, page: number): Promise<Record<string, unknown>> => {
|
||||||
|
return this.fetch('GET', `users-pending-deletion?limit=${limit}&page=${page}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'get user limits',
|
name: 'get user limits',
|
||||||
desc: 'Get the current limits for a user',
|
desc: 'Get the current limits for a user',
|
||||||
|
@ -206,6 +206,96 @@ func (server *Server) userInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
sendJSONData(w, http.StatusOK, data)
|
sendJSONData(w, http.StatusOK, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) usersPendingDeletion(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
FullName string `json:"fullName"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
query := r.URL.Query()
|
||||||
|
|
||||||
|
limitParam := query.Get("limit")
|
||||||
|
if limitParam == "" {
|
||||||
|
sendJSONError(w, "Bad request", "parameter 'limit' can't be empty", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, err := strconv.ParseUint(limitParam, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "Bad request", err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pageParam := query.Get("page")
|
||||||
|
if pageParam == "" {
|
||||||
|
sendJSONError(w, "Bad request", "parameter 'page' can't be empty", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
page, err := strconv.ParseUint(pageParam, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "Bad request", err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var sendingPage struct {
|
||||||
|
Users []User `json:"users"`
|
||||||
|
PageCount uint `json:"pageCount"`
|
||||||
|
CurrentPage uint `json:"currentPage"`
|
||||||
|
TotalCount uint64 `json:"totalCount"`
|
||||||
|
HasMore bool `json:"hasMore"`
|
||||||
|
}
|
||||||
|
usersPage, err := server.db.Console().Users().GetByStatus(
|
||||||
|
ctx, console.PendingDeletion, console.UserCursor{
|
||||||
|
Limit: uint(limit),
|
||||||
|
Page: uint(page),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed retrieving a usersPage of users", err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sendingPage.PageCount = usersPage.PageCount
|
||||||
|
sendingPage.CurrentPage = usersPage.CurrentPage
|
||||||
|
sendingPage.TotalCount = usersPage.TotalCount
|
||||||
|
sendingPage.Users = make([]User, 0, len(usersPage.Users))
|
||||||
|
|
||||||
|
if sendingPage.PageCount > sendingPage.CurrentPage {
|
||||||
|
sendingPage.HasMore = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range usersPage.Users {
|
||||||
|
invoices, err := server.payments.Invoices().ListFailed(ctx, &user.ID)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "getting invoices failed",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(invoices) != 0 {
|
||||||
|
sendingPage.TotalCount--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sendingPage.Users = append(sendingPage.Users, User{
|
||||||
|
ID: user.ID,
|
||||||
|
FullName: user.FullName,
|
||||||
|
Email: user.Email,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(sendingPage)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "json encoding failed",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sendJSONData(w, http.StatusOK, data)
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) userLimits(w http.ResponseWriter, r *http.Request) {
|
func (server *Server) userLimits(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ type Users interface {
|
|||||||
UpdateFailedLoginCountAndExpiration(ctx context.Context, failedLoginPenalty *float64, id uuid.UUID) error
|
UpdateFailedLoginCountAndExpiration(ctx context.Context, failedLoginPenalty *float64, id uuid.UUID) error
|
||||||
// GetByEmailWithUnverified is a method for querying users by email from the database.
|
// GetByEmailWithUnverified is a method for querying users by email from the database.
|
||||||
GetByEmailWithUnverified(ctx context.Context, email string) (*User, []User, error)
|
GetByEmailWithUnverified(ctx context.Context, email string) (*User, []User, error)
|
||||||
|
// GetByStatus is a method for querying user by status from the database.
|
||||||
|
GetByStatus(ctx context.Context, status UserStatus, cursor UserCursor) (*UsersPage, error)
|
||||||
// GetByEmail is a method for querying user by verified email from the database.
|
// GetByEmail is a method for querying user by verified email from the database.
|
||||||
GetByEmail(ctx context.Context, email string) (*User, error)
|
GetByEmail(ctx context.Context, email string) (*User, error)
|
||||||
// Insert is a method for inserting user into the database.
|
// Insert is a method for inserting user into the database.
|
||||||
@ -66,6 +68,24 @@ type UserInfo struct {
|
|||||||
ShortName string `json:"shortName"`
|
ShortName string `json:"shortName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserCursor holds info for user info cursor pagination.
|
||||||
|
type UserCursor struct {
|
||||||
|
Limit uint `json:"limit"`
|
||||||
|
Page uint `json:"page"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsersPage represent user info page result.
|
||||||
|
type UsersPage struct {
|
||||||
|
Users []User `json:"users"`
|
||||||
|
|
||||||
|
Limit uint `json:"limit"`
|
||||||
|
Offset uint64 `json:"offset"`
|
||||||
|
|
||||||
|
PageCount uint `json:"pageCount"`
|
||||||
|
CurrentPage uint `json:"currentPage"`
|
||||||
|
TotalCount uint64 `json:"totalCount"`
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid checks UserInfo validity and returns error describing whats wrong.
|
// IsValid checks UserInfo validity and returns error describing whats wrong.
|
||||||
// The returned error has the class ErrValiation.
|
// The returned error has the class ErrValiation.
|
||||||
func (user *UserInfo) IsValid() error {
|
func (user *UserInfo) IsValid() error {
|
||||||
|
@ -336,6 +336,53 @@ func TestGetUserByEmail(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetUsersByStatus(t *testing.T) {
|
||||||
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||||
|
usersRepo := db.Console().Users()
|
||||||
|
|
||||||
|
inactiveUser := console.User{
|
||||||
|
ID: testrand.UUID(),
|
||||||
|
FullName: "Inactive User",
|
||||||
|
Email: email,
|
||||||
|
PasswordHash: []byte("123a123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := usersRepo.Insert(ctx, &inactiveUser)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
activeUser := console.User{
|
||||||
|
ID: testrand.UUID(),
|
||||||
|
FullName: "Active User",
|
||||||
|
Email: email,
|
||||||
|
Status: console.Active,
|
||||||
|
PasswordHash: []byte("123a123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = usersRepo.Insert(ctx, &activeUser)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Required to set the active status.
|
||||||
|
err = usersRepo.Update(ctx, activeUser.ID, console.UpdateUserRequest{
|
||||||
|
Status: &activeUser.Status,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cursor := console.UserCursor{
|
||||||
|
Limit: 50,
|
||||||
|
Page: 1,
|
||||||
|
}
|
||||||
|
usersPage, err := usersRepo.GetByStatus(ctx, console.Inactive, cursor)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Lenf(t, usersPage.Users, 1, "expected 1 inactive user")
|
||||||
|
require.Equal(t, inactiveUser.ID, usersPage.Users[0].ID)
|
||||||
|
|
||||||
|
usersPage, err = usersRepo.GetByStatus(ctx, console.Active, cursor)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Lenf(t, usersPage.Users, 1, "expected 1 active user")
|
||||||
|
require.Equal(t, activeUser.ID, usersPage.Users[0].ID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetUnverifiedNeedingReminder(t *testing.T) {
|
func TestGetUnverifiedNeedingReminder(t *testing.T) {
|
||||||
testplanet.Run(t, testplanet.Config{
|
testplanet.Run(t, testplanet.Config{
|
||||||
Reconfigure: testplanet.Reconfigure{
|
Reconfigure: testplanet.Reconfigure{
|
||||||
|
@ -11992,6 +11992,12 @@ type CustomerId_Row struct {
|
|||||||
CustomerId string
|
CustomerId string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Id_Email_FullName_Row struct {
|
||||||
|
Id []byte
|
||||||
|
Email string
|
||||||
|
FullName string
|
||||||
|
}
|
||||||
|
|
||||||
type Id_PieceCount_Row struct {
|
type Id_PieceCount_Row struct {
|
||||||
Id []byte
|
Id []byte
|
||||||
PieceCount int64
|
PieceCount int64
|
||||||
@ -16871,6 +16877,77 @@ func (obj *pgxImpl) Get_User_ProjectStorageLimit_User_ProjectBandwidthLimit_User
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *pgxImpl) Count_User_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field) (
|
||||||
|
count int64, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literal("SELECT COUNT(*) FROM users WHERE users.status = ?")
|
||||||
|
|
||||||
|
var __values []interface{}
|
||||||
|
__values = append(__values, user_status.value())
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
err = obj.queryRowContext(ctx, __stmt, __values...).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *pgxImpl) Limited_User_Id_User_Email_User_FullName_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field,
|
||||||
|
limit int, offset int64) (
|
||||||
|
rows []*Id_Email_FullName_Row, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literal("SELECT users.id, users.email, users.full_name FROM users WHERE users.status = ? LIMIT ? OFFSET ?")
|
||||||
|
|
||||||
|
var __values []interface{}
|
||||||
|
__values = append(__values, user_status.value())
|
||||||
|
|
||||||
|
__values = append(__values, limit, offset)
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
for {
|
||||||
|
rows, err = func() (rows []*Id_Email_FullName_Row, err error) {
|
||||||
|
__rows, err := obj.driver.QueryContext(ctx, __stmt, __values...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer __rows.Close()
|
||||||
|
|
||||||
|
for __rows.Next() {
|
||||||
|
row := &Id_Email_FullName_Row{}
|
||||||
|
err = __rows.Scan(&row.Id, &row.Email, &row.FullName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
err = __rows.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
if obj.shouldRetry(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *pgxImpl) All_WebappSession_By_UserId(ctx context.Context,
|
func (obj *pgxImpl) All_WebappSession_By_UserId(ctx context.Context,
|
||||||
webapp_session_user_id WebappSession_UserId_Field) (
|
webapp_session_user_id WebappSession_UserId_Field) (
|
||||||
rows []*WebappSession, err error) {
|
rows []*WebappSession, err error) {
|
||||||
@ -25188,6 +25265,77 @@ func (obj *pgxcockroachImpl) Get_User_ProjectStorageLimit_User_ProjectBandwidthL
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *pgxcockroachImpl) Count_User_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field) (
|
||||||
|
count int64, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literal("SELECT COUNT(*) FROM users WHERE users.status = ?")
|
||||||
|
|
||||||
|
var __values []interface{}
|
||||||
|
__values = append(__values, user_status.value())
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
err = obj.queryRowContext(ctx, __stmt, __values...).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *pgxcockroachImpl) Limited_User_Id_User_Email_User_FullName_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field,
|
||||||
|
limit int, offset int64) (
|
||||||
|
rows []*Id_Email_FullName_Row, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literal("SELECT users.id, users.email, users.full_name FROM users WHERE users.status = ? LIMIT ? OFFSET ?")
|
||||||
|
|
||||||
|
var __values []interface{}
|
||||||
|
__values = append(__values, user_status.value())
|
||||||
|
|
||||||
|
__values = append(__values, limit, offset)
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
for {
|
||||||
|
rows, err = func() (rows []*Id_Email_FullName_Row, err error) {
|
||||||
|
__rows, err := obj.driver.QueryContext(ctx, __stmt, __values...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer __rows.Close()
|
||||||
|
|
||||||
|
for __rows.Next() {
|
||||||
|
row := &Id_Email_FullName_Row{}
|
||||||
|
err = __rows.Scan(&row.Id, &row.Email, &row.FullName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
err = __rows.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
if obj.shouldRetry(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *pgxcockroachImpl) All_WebappSession_By_UserId(ctx context.Context,
|
func (obj *pgxcockroachImpl) All_WebappSession_By_UserId(ctx context.Context,
|
||||||
webapp_session_user_id WebappSession_UserId_Field) (
|
webapp_session_user_id WebappSession_UserId_Field) (
|
||||||
rows []*WebappSession, err error) {
|
rows []*WebappSession, err error) {
|
||||||
@ -28875,6 +29023,10 @@ type Methods interface {
|
|||||||
bucket_metainfo_project_id BucketMetainfo_ProjectId_Field) (
|
bucket_metainfo_project_id BucketMetainfo_ProjectId_Field) (
|
||||||
count int64, err error)
|
count int64, err error)
|
||||||
|
|
||||||
|
Count_User_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field) (
|
||||||
|
count int64, err error)
|
||||||
|
|
||||||
CreateNoReturn_BillingBalance(ctx context.Context,
|
CreateNoReturn_BillingBalance(ctx context.Context,
|
||||||
billing_balance_user_id BillingBalance_UserId_Field,
|
billing_balance_user_id BillingBalance_UserId_Field,
|
||||||
billing_balance_balance BillingBalance_Balance_Field) (
|
billing_balance_balance BillingBalance_Balance_Field) (
|
||||||
@ -29481,6 +29633,11 @@ type Methods interface {
|
|||||||
limit int, offset int64) (
|
limit int, offset int64) (
|
||||||
rows []*StorjscanPayment, err error)
|
rows []*StorjscanPayment, err error)
|
||||||
|
|
||||||
|
Limited_User_Id_User_Email_User_FullName_By_Status(ctx context.Context,
|
||||||
|
user_status User_Status_Field,
|
||||||
|
limit int, offset int64) (
|
||||||
|
rows []*Id_Email_FullName_Row, err error)
|
||||||
|
|
||||||
Paged_BucketBandwidthRollupArchive_By_IntervalStart_GreaterOrEqual(ctx context.Context,
|
Paged_BucketBandwidthRollupArchive_By_IntervalStart_GreaterOrEqual(ctx context.Context,
|
||||||
bucket_bandwidth_rollup_archive_interval_start_greater_or_equal BucketBandwidthRollupArchive_IntervalStart_Field,
|
bucket_bandwidth_rollup_archive_interval_start_greater_or_equal BucketBandwidthRollupArchive_IntervalStart_Field,
|
||||||
limit int, start *Paged_BucketBandwidthRollupArchive_By_IntervalStart_GreaterOrEqual_Continuation) (
|
limit int, start *Paged_BucketBandwidthRollupArchive_By_IntervalStart_GreaterOrEqual_Continuation) (
|
||||||
|
@ -112,6 +112,16 @@ read one (
|
|||||||
where user.id = ?
|
where user.id = ?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
read count (
|
||||||
|
select user
|
||||||
|
where user.status = ?
|
||||||
|
)
|
||||||
|
|
||||||
|
read limitoffset (
|
||||||
|
select user.id user.email user.full_name
|
||||||
|
where user.status = ?
|
||||||
|
)
|
||||||
|
|
||||||
model webapp_session (
|
model webapp_session (
|
||||||
key id
|
key id
|
||||||
index ( fields user_id )
|
index ( fields user_id )
|
||||||
|
@ -88,6 +88,71 @@ func (users *users) GetByEmailWithUnverified(ctx context.Context, email string)
|
|||||||
return verified, unverified, errors.Err()
|
return verified, unverified, errors.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (users *users) GetByStatus(ctx context.Context, status console.UserStatus, cursor console.UserCursor) (page *console.UsersPage, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
if cursor.Limit == 0 {
|
||||||
|
return nil, Error.New("limit cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor.Page == 0 {
|
||||||
|
return nil, Error.New("page cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
page = &console.UsersPage{
|
||||||
|
Limit: cursor.Limit,
|
||||||
|
Offset: uint64((cursor.Page - 1) * cursor.Limit),
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := users.db.Count_User_By_Status(ctx, dbx.User_Status(int(status)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
page.TotalCount = uint64(count)
|
||||||
|
|
||||||
|
if page.TotalCount == 0 {
|
||||||
|
return page, nil
|
||||||
|
}
|
||||||
|
if page.Offset > page.TotalCount-1 {
|
||||||
|
return nil, Error.New("page is out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
dbxUsers, err := users.db.Limited_User_Id_User_Email_User_FullName_By_Status(ctx,
|
||||||
|
dbx.User_Status(int(status)),
|
||||||
|
int(page.Limit), int64(page.Offset))
|
||||||
|
if err != nil {
|
||||||
|
if errs.Is(err, sql.ErrNoRows) {
|
||||||
|
return &console.UsersPage{
|
||||||
|
Users: []console.User{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, usr := range dbxUsers {
|
||||||
|
id, err := uuid.FromBytes(usr.Id)
|
||||||
|
if err != nil {
|
||||||
|
return &console.UsersPage{
|
||||||
|
Users: []console.User{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
page.Users = append(page.Users, console.User{
|
||||||
|
ID: id,
|
||||||
|
Email: usr.Email,
|
||||||
|
FullName: usr.FullName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
page.PageCount = uint(page.TotalCount / uint64(cursor.Limit))
|
||||||
|
if page.TotalCount%uint64(cursor.Limit) != 0 {
|
||||||
|
page.PageCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
page.CurrentPage = cursor.Page
|
||||||
|
|
||||||
|
return page, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetByEmail is a method for querying user by verified email from the database.
|
// GetByEmail is a method for querying user by verified email from the database.
|
||||||
func (users *users) GetByEmail(ctx context.Context, email string) (_ *console.User, err error) {
|
func (users *users) GetByEmail(ctx context.Context, email string) (_ *console.User, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
Loading…
Reference in New Issue
Block a user