add mass_grant_credits helper script and other prep for premium (#196)
* add mass_grant_credits helper script * include error in the rate limit log * allow granting 0 credits * migration to change default user_tier to premium
This commit is contained in:
parent
b80f994e90
commit
b1f447c5c8
@ -39,6 +39,7 @@ mod m20230708_151756_rpc_accounting_free_usage_credits;
|
|||||||
mod m20230708_152131_referral_track_one_time_bonus_bonus;
|
mod m20230708_152131_referral_track_one_time_bonus_bonus;
|
||||||
mod m20230713_144446_stripe_default_date_created;
|
mod m20230713_144446_stripe_default_date_created;
|
||||||
mod m20230713_210511_deposit_add_date_created;
|
mod m20230713_210511_deposit_add_date_created;
|
||||||
|
mod m20230726_072845_default_premium_user_tier;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20230708_152131_referral_track_one_time_bonus_bonus::Migration),
|
Box::new(m20230708_152131_referral_track_one_time_bonus_bonus::Migration),
|
||||||
Box::new(m20230713_144446_stripe_default_date_created::Migration),
|
Box::new(m20230713_144446_stripe_default_date_created::Migration),
|
||||||
Box::new(m20230713_210511_deposit_add_date_created::Migration),
|
Box::new(m20230713_210511_deposit_add_date_created::Migration),
|
||||||
|
Box::new(m20230726_072845_default_premium_user_tier::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
90
migration/src/m20230726_072845_default_premium_user_tier.rs
Normal file
90
migration/src/m20230726_072845_default_premium_user_tier.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
/// change default to premium tier
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
let db_conn = manager.get_connection();
|
||||||
|
let db_backend = manager.get_database_backend();
|
||||||
|
|
||||||
|
let select_premium_id = Query::select()
|
||||||
|
.column(UserTier::Id)
|
||||||
|
.column(UserTier::Title)
|
||||||
|
.from(UserTier::Table)
|
||||||
|
.and_having(Expr::col(UserTier::Title).eq("Premium"))
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let premium_id: u64 = db_conn
|
||||||
|
.query_one(db_backend.build(&select_premium_id))
|
||||||
|
.await?
|
||||||
|
.expect("Premium tier should exist")
|
||||||
|
.try_get("", &UserTier::Id.to_string())?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(User::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(User::UserTierId)
|
||||||
|
.big_unsigned()
|
||||||
|
.default(premium_id)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// change default to free tier
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
let db_conn = manager.get_connection();
|
||||||
|
let db_backend = manager.get_database_backend();
|
||||||
|
|
||||||
|
let select_free_id = Query::select()
|
||||||
|
.column(UserTier::Id)
|
||||||
|
.column(UserTier::Title)
|
||||||
|
.from(UserTier::Table)
|
||||||
|
.and_having(Expr::col(UserTier::Title).eq("Free"))
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let free_id: u64 = db_conn
|
||||||
|
.query_one(db_backend.build(&select_free_id))
|
||||||
|
.await?
|
||||||
|
.expect("Free tier should exist")
|
||||||
|
.try_get("", &UserTier::Id.to_string())?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(User::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(User::UserTierId)
|
||||||
|
.big_unsigned()
|
||||||
|
.default(free_id)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
enum User {
|
||||||
|
Table,
|
||||||
|
UserTierId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
enum UserTier {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Title,
|
||||||
|
}
|
@ -68,6 +68,7 @@ enum SubCommand {
|
|||||||
CreateKey(sub_commands::CreateKeySubCommand),
|
CreateKey(sub_commands::CreateKeySubCommand),
|
||||||
CreateUser(sub_commands::CreateUserSubCommand),
|
CreateUser(sub_commands::CreateUserSubCommand),
|
||||||
DropMigrationLock(sub_commands::DropMigrationLockSubCommand),
|
DropMigrationLock(sub_commands::DropMigrationLockSubCommand),
|
||||||
|
MassGrantCredits(sub_commands::MassGrantCredits),
|
||||||
MigrateStatsToV2(sub_commands::MigrateStatsToV2SubCommand),
|
MigrateStatsToV2(sub_commands::MigrateStatsToV2SubCommand),
|
||||||
Pagerduty(sub_commands::PagerdutySubCommand),
|
Pagerduty(sub_commands::PagerdutySubCommand),
|
||||||
PopularityContest(sub_commands::PopularityContestSubCommand),
|
PopularityContest(sub_commands::PopularityContestSubCommand),
|
||||||
@ -392,6 +393,15 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
x.main(&db_conn).await
|
x.main(&db_conn).await
|
||||||
}
|
}
|
||||||
|
SubCommand::MassGrantCredits(x) => {
|
||||||
|
let db_url = cli_config
|
||||||
|
.db_url
|
||||||
|
.expect("'--config' (with a db) or '--db-url' is required to run mass_grant_credits");
|
||||||
|
|
||||||
|
let db_conn = get_migrated_db(db_url, 1, 1).await?;
|
||||||
|
|
||||||
|
x.main(&db_conn).await
|
||||||
|
}
|
||||||
SubCommand::MigrateStatsToV2(x) => {
|
SubCommand::MigrateStatsToV2(x) => {
|
||||||
|
|
||||||
let top_config = top_config.expect("--config is required to run the migration from stats-mysql to stats-influx");
|
let top_config = top_config.expect("--config is required to run the migration from stats-mysql to stats-influx");
|
||||||
|
@ -298,9 +298,9 @@ impl OpenRequestHandle {
|
|||||||
let retry_at = Instant::now() + Duration::from_secs(1);
|
let retry_at = Instant::now() + Duration::from_secs(1);
|
||||||
|
|
||||||
if self.rpc.backup {
|
if self.rpc.backup {
|
||||||
debug!(?retry_at, "rate limited on {}!", self.rpc);
|
debug!(?retry_at, ?err, "rate limited on {}!", self.rpc);
|
||||||
} else {
|
} else {
|
||||||
warn!(?retry_at, "rate limited on {}!", self.rpc);
|
warn!(?retry_at, ?err, "rate limited on {}!", self.rpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
hard_limit_until.send_replace(retry_at);
|
hard_limit_until.send_replace(retry_at);
|
||||||
|
81
web3_proxy/src/sub_commands/mass_grant_credits.rs
Normal file
81
web3_proxy/src/sub_commands/mass_grant_credits.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// TODO: a lot of this is copy/paste of the admin frontend endpoint for granting credits.
|
||||||
|
// that's easier than refactoring right now.
|
||||||
|
// it could be cleaned up, but this is a script that runs once so isn't worth spending tons of time on.
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use argh::FromArgs;
|
||||||
|
use entities::{admin_increase_balance_receipt, user, user_tier};
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use migration::sea_orm::{
|
||||||
|
self, ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel,
|
||||||
|
PaginatorTrait, QueryFilter, QueryOrder, TransactionTrait,
|
||||||
|
};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
/// Grant credits to all the users in a tier (and change their tier to premium).
|
||||||
|
#[argh(subcommand, name = "mass_grant_credits")]
|
||||||
|
pub struct MassGrantCredits {
|
||||||
|
#[argh(positional)]
|
||||||
|
/// the name of the user tier whose users will be upgraded to premium
|
||||||
|
tier_to_upgrade: String,
|
||||||
|
|
||||||
|
#[argh(positional)]
|
||||||
|
/// how many credits to give.
|
||||||
|
credits: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MassGrantCredits {
|
||||||
|
pub async fn main(self, db_conn: &DatabaseConnection) -> anyhow::Result<()> {
|
||||||
|
let old_user_tier = user_tier::Entity::find()
|
||||||
|
.filter(user_tier::Column::Title.like(&self.tier_to_upgrade))
|
||||||
|
.one(db_conn)
|
||||||
|
.await?
|
||||||
|
.context("no user tier found with that name")?;
|
||||||
|
|
||||||
|
let new_user_tier = user_tier::Entity::find()
|
||||||
|
.filter(user_tier::Column::Title.like("Premium"))
|
||||||
|
.one(db_conn)
|
||||||
|
.await?
|
||||||
|
.context("no Premium user tier found")?;
|
||||||
|
|
||||||
|
let mut user_stream = user::Entity::find()
|
||||||
|
.filter(user::Column::UserTierId.eq(old_user_tier.id))
|
||||||
|
.order_by_asc(user::Column::Id)
|
||||||
|
.paginate(db_conn, 50)
|
||||||
|
.into_stream();
|
||||||
|
|
||||||
|
while let Some(users_to_upgrade) = user_stream.try_next().await? {
|
||||||
|
let txn = db_conn.begin().await?;
|
||||||
|
|
||||||
|
for user_to_upgrade in users_to_upgrade {
|
||||||
|
if self.credits > 0.into() {
|
||||||
|
let increase_balance_receipt = admin_increase_balance_receipt::ActiveModel {
|
||||||
|
amount: sea_orm::Set(self.credits),
|
||||||
|
// TODO: allow customizing the admin id
|
||||||
|
admin_id: sea_orm::Set(1),
|
||||||
|
deposit_to_user_id: sea_orm::Set(user_to_upgrade.id),
|
||||||
|
note: sea_orm::Set("mass grant credits".into()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
increase_balance_receipt.save(&txn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut user_to_upgrade = user_to_upgrade.into_active_model();
|
||||||
|
|
||||||
|
user_to_upgrade.user_tier_id = sea_orm::Set(new_user_tier.id);
|
||||||
|
|
||||||
|
user_to_upgrade.save(&txn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit().await?;
|
||||||
|
|
||||||
|
// we can't invalidate balance caches because they are in another process. they do have short ttls though
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("success");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ mod count_users;
|
|||||||
mod create_key;
|
mod create_key;
|
||||||
mod create_user;
|
mod create_user;
|
||||||
mod drop_migration_lock;
|
mod drop_migration_lock;
|
||||||
|
mod mass_grant_credits;
|
||||||
mod migrate_stats_to_v2;
|
mod migrate_stats_to_v2;
|
||||||
mod pagerduty;
|
mod pagerduty;
|
||||||
mod popularity_contest;
|
mod popularity_contest;
|
||||||
@ -29,6 +30,7 @@ pub use self::count_users::CountUsersSubCommand;
|
|||||||
pub use self::create_key::CreateKeySubCommand;
|
pub use self::create_key::CreateKeySubCommand;
|
||||||
pub use self::create_user::CreateUserSubCommand;
|
pub use self::create_user::CreateUserSubCommand;
|
||||||
pub use self::drop_migration_lock::DropMigrationLockSubCommand;
|
pub use self::drop_migration_lock::DropMigrationLockSubCommand;
|
||||||
|
pub use self::mass_grant_credits::MassGrantCredits;
|
||||||
pub use self::migrate_stats_to_v2::MigrateStatsToV2SubCommand;
|
pub use self::migrate_stats_to_v2::MigrateStatsToV2SubCommand;
|
||||||
pub use self::pagerduty::PagerdutySubCommand;
|
pub use self::pagerduty::PagerdutySubCommand;
|
||||||
pub use self::popularity_contest::PopularityContestSubCommand;
|
pub use self::popularity_contest::PopularityContestSubCommand;
|
||||||
|
Loading…
Reference in New Issue
Block a user