diff --git a/rust/api-server/api/Cargo.toml b/rust/api-server/api/Cargo.toml index c7d169a..245ef88 100644 --- a/rust/api-server/api/Cargo.toml +++ b/rust/api-server/api/Cargo.toml @@ -24,7 +24,7 @@ tower = "0.5.2" service = { path = "../service" } entity = { path = "../entity" } migration = { path = "../migration" } -tracing-subscriber = { version = "0.3.19", features = ["json"] } +tracing-subscriber = { version = "0.3.19", features = ["json", "serde", "serde_json"] } tower-http = { version = "0.6.2", features = ["fs", "cors", "compression-full"] } tower-cookies = "0.11.0" dotenvy = "0.15.7" diff --git a/rust/api-server/api/src/config.rs b/rust/api-server/api/src/config.rs index bfb3ca5..f6c86b9 100644 --- a/rust/api-server/api/src/config.rs +++ b/rust/api-server/api/src/config.rs @@ -26,6 +26,7 @@ pub(crate) struct Config { pub(crate) import_enabled: bool, #[serde(rename = "enabledimporter")] pub(crate) enabled_importer: Vec, + pub download: DownloadConfig, } impl Default for Config { @@ -44,6 +45,265 @@ impl Default for Config { import_type: ImportType::File, import_enabled: false, enabled_importer: vec!["".to_string()], + download: DownloadConfig::default(), + } + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct DownloadConfig { + pub desc_tables: Vec, + pub state_tables: Vec, + pub rest_tables: Vec, +} + +impl Default for DownloadConfig { + fn default() -> Self { + Self { + desc_tables: vec![ + "achievement_desc".to_string(), + "alert_desc".to_string(), + "biome_desc".to_string(), + "buff_desc".to_string(), + "buff_type_desc".to_string(), + "building_claim_desc".to_string(), + "building_desc".to_string(), + "building_function_type_mapping_desc".to_string(), + "building_portal_desc".to_string(), + "building_repairs_desc".to_string(), + "building_spawn_desc".to_string(), + "building_type_desc".to_string(), + "cargo_desc".to_string(), + "character_stat_desc".to_string(), + "chest_rarity_desc".to_string(), + "claimTech_desc".to_string(), + "climb_requirement_desc".to_string(), + "clothing_desc".to_string(), + "collectible_desc".to_string(), + "combat_action_desc".to_string(), + "construction_recipe_desc".to_string(), + "crafting_recipe_desc".to_string(), + "deconstruction_recipe_desc".to_string(), + "deployable_desc".to_string(), + "elevator_desc".to_string(), + "emote_desc".to_string(), + "empire_color_desc".to_string(), + "empire_notification_desc".to_string(), + "empire_rank_desc".to_string(), + "empire_supplies_desc".to_string(), + "empire_territory_desc".to_string(), + "enemy_ai_params_desc".to_string(), + "enemy_desc".to_string(), + "environment_debuff_desc".to_string(), + "equipment_desc".to_string(), + "extraction_recipe_desc".to_string(), + "food_desc".to_string(), + "gate_desc".to_string(), + "interior_instance_desc".to_string(), + "interior_network_desc".to_string(), + "interior_portal_connections_desc".to_string(), + "interior_shape_desc".to_string(), + "interior_spawn_desc".to_string(), + "item_conversion_recipe_desc".to_string(), + "item_desc".to_string(), + "item_list_desc".to_string(), + "knowledge_scroll_desc".to_string(), + "knowledge_scroll_type_desc".to_string(), + "loot_chest_desc".to_string(), + "loot_rarity_desc".to_string(), + "loot_table_desc".to_string(), + "npc_desc".to_string(), + "onboarding_reward_desc".to_string(), + "parameters_desc".to_string(), + "pathfinding_desc".to_string(), + "paving_tile_desc".to_string(), + "player_action_desc".to_string(), + "private_parameters_desc".to_string(), + "resource_clump_desc".to_string(), + "resource_desc".to_string(), + "resource_growth_recipe_desc".to_string(), + "resource_placement_recipe_desc".to_string(), + "secondary_knowledge_desc".to_string(), + "single_resource_to_clump_desc".to_string(), + "skill_desc".to_string(), + "targeting_matrix_desc".to_string(), + "teleport_item_desc".to_string(), + "terraform_recipe_desc".to_string(), + "tool_desc".to_string(), + "tool_type_desc".to_string(), + "traveler_trade_order_desc".to_string(), + "wall_desc".to_string(), + "weapon_desc".to_string(), + "weapon_type_desc".to_string(), + ], + state_tables: vec![ + "ai_debug_state".to_string(), + "action_state".to_string(), + "active_buff_state".to_string(), + "admin_restore_player_state_timer".to_string(), + "alert_state".to_string(), + "attached_herds_state".to_string(), + "attack_outcome_state".to_string(), + "auto_claim_state".to_string(), + "barter_stall_state".to_string(), + "building_state".to_string(), + "cargo_state".to_string(), + "character_stats_state".to_string(), + "chat_message_state".to_string(), + "claim_description_state".to_string(), + "claim_recruitment_state".to_string(), + "claim_tech_state".to_string(), + "claim_tile_state".to_string(), + "combat_state".to_string(), + "deployable_collectible_state".to_string(), + "deployable_state".to_string(), + "dimension_description_state".to_string(), + "dimension_network_state".to_string(), + "empire_chunk_state".to_string(), + "empire_expansion_state".to_string(), + "empire_foundry_state".to_string(), + "empire_log_state".to_string(), + "empire_node_siege_state".to_string(), + "empire_node_state".to_string(), + "empire_notification_state".to_string(), + "empire_player_data_state".to_string(), + "empire_player_log_state".to_string(), + "empire_rank_state".to_string(), + "empire_settlement_state".to_string(), + "empire_siege_engine_state".to_string(), + "empire_state".to_string(), + "enemy_mob_monitor_state".to_string(), + "enemy_state".to_string(), + "equipment_state".to_string(), + "experience_state".to_string(), + "exploration_chunks_state".to_string(), + "footprint_tile_state".to_string(), + "global_search_state".to_string(), + "growth_state".to_string(), + "health_state".to_string(), + "herd_state".to_string(), + "interior_collapse_trigger_state".to_string(), + "inventory_state".to_string(), + "item_pile_state".to_string(), + "knowledge_achievement_state".to_string(), + "knowledge_battle_action_state".to_string(), + "knowledge_building_state".to_string(), + "knowledge_cargo_state".to_string(), + "knowledge_construction_state".to_string(), + "knowledge_craft_state".to_string(), + "knowledge_deployable_state".to_string(), + "knowledge_enemy_state".to_string(), + "knowledge_extract_state".to_string(), + "knowledge_item_state".to_string(), + "knowledge_lore_state".to_string(), + "knowledge_npc_state".to_string(), + "knowledge_paving_state".to_string(), + "knowledge_resource_placement_state".to_string(), + "knowledge_resource_state".to_string(), + "knowledge_ruins_state".to_string(), + "knowledge_secondary_state".to_string(), + "knowledge_vault_state".to_string(), + "light_source_state".to_string(), + "location_state".to_string(), + "loot_chest_state".to_string(), + "mobile_entity_state".to_string(), + "mounting_state".to_string(), + "move_validation_strike_counter_state".to_string(), + "npc_state".to_string(), + "onboarding_state".to_string(), + "passive_craft_state".to_string(), + "paved_tile_state".to_string(), + "player_action_state".to_string(), + "player_lowercase_username_state".to_string(), + "player_note_state".to_string(), + "player_prefs_state".to_string(), + "player_state".to_string(), + "player_timestamp_state".to_string(), + "player_username_state".to_string(), + "player_vote_state".to_string(), + "portal_state".to_string(), + "progressive_action_state".to_string(), + "project_site_state".to_string(), + "rent_state".to_string(), + "resource_state".to_string(), + "satiation_state".to_string(), + "signed_in_player_state".to_string(), + "signed_in_user_state".to_string(), + "stamina_state".to_string(), + "starving_player_state".to_string(), + "target_state".to_string(), + "targetable_state".to_string(), + "terraform_progress_state".to_string(), + "terrain_chunk_state".to_string(), + "threat_state".to_string(), + "toolbar_state".to_string(), + "trade_order_state".to_string(), + "trade_session_state".to_string(), + "unclaimed_collectibles_state".to_string(), + "unclaimed_shards_state".to_string(), + "user_moderation_state".to_string(), + "user_sign_in_state".to_string(), + "user_state".to_string(), + "vault_state".to_string(), + ], + rest_tables: vec![ + "admin_broadcast".to_string(), + "attack_impact_timer".to_string(), + "attack_timer".to_string(), + "auto_logout_loop_timer".to_string(), + "building_decay_loop_timer".to_string(), + "building_despawn_timer".to_string(), + "cargo_despawn_timer".to_string(), + "cargo_spawn_timer".to_string(), + "chat_cache".to_string(), + "claim_tech_unlock_timer".to_string(), + "claim_tile_cost".to_string(), + "collect_stats_timer".to_string(), + "config".to_string(), + "day_night_loop_timer".to_string(), + "deployable_dismount_timer".to_string(), + "destroy_dimension_network_timer".to_string(), + "empire_craft_supplies_timer".to_string(), + "empire_decay_loop_timer".to_string(), + "empire_siege_loop_timer".to_string(), + "end_grace_period_timer".to_string(), + "enemy_despawn_timer".to_string(), + "enemy_regen_loop_timer".to_string(), + "environment_debuff_loop_timer".to_string(), + "force_generate_types".to_string(), + "globals".to_string(), + "globals_appeared".to_string(), + "growth_loop_timer".to_string(), + "hide_deployable_timer".to_string(), + "identity_role".to_string(), + "interior_set_collapsed_timer".to_string(), + "item_pile_despawn_timer".to_string(), + "location_cache".to_string(), + "loot_chest_despawn_timer".to_string(), + "loot_chest_spawn_timer".to_string(), + "npc_ai_loop_timer".to_string(), + "passive_craft_timer".to_string(), + "player_death_timer".to_string(), + "player_regen_loop_timer".to_string(), + "player_respawn_after_death_timer".to_string(), + "player_use_elevator_timer".to_string(), + "player_vote_conclude_timer".to_string(), + "rent_collector_loop_timer".to_string(), + "rent_evict_timer".to_string(), + "reset_chunk_index_timer".to_string(), + "reset_mobile_entity_timer".to_string(), + "resource_count".to_string(), + "resource_spawn_timer".to_string(), + "resources_log".to_string(), + "resources_regen_loop_timer".to_string(), + "respawn_resource_in_chunk_timer".to_string(), + "server_identity".to_string(), + "single_resource_clump_info".to_string(), + "staged_static_data".to_string(), + "starving_loop_timer".to_string(), + "teleport_player_timer".to_string(), + "trade_session_loop_timer".to_string(), + ], } } } diff --git a/rust/api-server/api/src/download.rs b/rust/api-server/api/src/download.rs new file mode 100644 index 0000000..c0f8fda --- /dev/null +++ b/rust/api-server/api/src/download.rs @@ -0,0 +1,117 @@ +use crate::config::Config; +use log::error; +use reqwest::Client; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use tracing::info; + +#[derive(clap::Subcommand, PartialEq, Eq, Debug)] +pub enum DownloadSubcommand { + All, + State, + Desc, + Rest, +} + +pub async fn download_desc_tables(client: &Client, storage_path: &Path, config: &Config) { + for table in &config.download.desc_tables { + let desc_result = download_tables(client, table, config, storage_path, "desc").await; + + if let Err(error) = desc_result { + error!("Error while downloading desc table {table}: {error}"); + } + } +} + +pub async fn download_state_tables(client: &Client, storage_path: &Path, config: &Config) { + for table in &config.download.state_tables { + let state_result = download_tables(client, table, config, storage_path, "state").await; + + if let Err(error) = state_result { + error!("Error while downloading state table {table}: {error}"); + } + } +} + +pub async fn download_rest_tables(client: &Client, storage_path: &Path, config: &Config) { + for table in &config.download.rest_tables { + let rest_result = download_tables(client, table, config, storage_path, "rest").await; + + if let Err(error) = rest_result { + error!("Error while downloading rest table {table}: {error}"); + } + } +} + +pub async fn download_all_tables( + download_subcommand: DownloadSubcommand, + client: &Client, + storage_path: &Path, + config: &Config, +) { + match download_subcommand { + DownloadSubcommand::All => { + download_desc_tables(client, storage_path, config).await; + download_state_tables(client, storage_path, config).await; + download_rest_tables(client, storage_path, config).await; + } + DownloadSubcommand::Desc => { + download_desc_tables(client, storage_path, config).await; + } + DownloadSubcommand::State => { + download_state_tables(client, storage_path, config).await; + } + DownloadSubcommand::Rest => { + download_rest_tables(client, storage_path, config).await; + } + } +} + +/// Donwload the table and save it to the storage path with the type as the folder before the name +pub async fn download_tables( + client: &Client, + table: &str, + config: &Config, + storage_path: &Path, + folder: &str, +) -> anyhow::Result<()> { + let domain = &config.spacetimedb.domain; + let protocol = &config.spacetimedb.protocol; + let database = &config.spacetimedb.database; + + let response = client + .post(format!("{protocol}{domain}/database/sql/{database}")) + .body(format!("SELECT * FROM {table}")) + .send() + .await; + + let json = match response { + Ok(response) => { + if !response.status().is_success() { + let error = response.text().await?; + error!("Error: {error}"); + return Err(anyhow::anyhow!("Error: {error}")); + } + + response.text().await? + } + Err(error) => { + error!("Error: {error}"); + return Err(anyhow::anyhow!("Error: {error}")); + } + }; + + let folder_to_create = storage_path.join(folder); + if !folder_to_create.exists() { + std::fs::create_dir_all(&folder_to_create)?; + } + let path = storage_path.join(format!("{folder}/{table}.json")); + let mut file = File::create(&path)?; + + info!("Saving to {path:?}"); + + file.write_all(json.as_bytes())?; + + Ok(()) +} diff --git a/rust/api-server/api/src/lib.rs b/rust/api-server/api/src/lib.rs index 791c670..d24dc56 100644 --- a/rust/api-server/api/src/lib.rs +++ b/rust/api-server/api/src/lib.rs @@ -6,6 +6,7 @@ mod claims; mod collectible_desc; mod config; mod deployable_state; +mod download; mod inventory; mod items; mod items_and_cargo; @@ -50,8 +51,6 @@ use service::sea_orm::{Database, DatabaseConnection}; use std::collections::{HashMap, HashSet}; use std::env; use std::fmt::Display; -use std::fs::File; -use std::io::Write; use std::ops::{AddAssign, SubAssign}; use std::path::{Path, PathBuf}; use std::process::exit; @@ -71,19 +70,6 @@ async fn start(database_connection: DatabaseConnection, config: Config) -> anyho Migrator::up(&database_connection, None).await?; - if env::var("DOWNLOAD_ALL_TABLES").is_ok() { - let client = create_default_client(config.clone()); - - download_all_tables( - &client, - &config.spacetimedb.domain.clone(), - &config.spacetimedb.protocol.clone(), - &config.spacetimedb.database.clone(), - Path::new(&config.storage_path.clone()), - ) - .await; - } - if config.import_enabled { import_data(config.clone()); } @@ -938,361 +924,6 @@ async fn track_metrics(req: Request, next: Next) -> impl IntoResponse { response } -pub async fn download_all_tables( - client: &Client, - domain: &str, - protocol: &str, - database: &str, - storage_path: &Path, -) { - let desc_tables = vec![ - "AchievementDesc", - "AlertDesc", - "BiomeDesc", - "BuffDesc", - "BuffTypeDesc", - "BuildingClaimDesc", - "BuildingDesc", - "BuildingFunctionTypeMappingDesc", - "BuildingPortalDesc", - "BuildingRepairsDesc", - "BuildingSpawnDesc", - "BuildingTypeDesc", - "CargoDesc", - "CharacterStatDesc", - "ChestRarityDesc", - "ClaimDescriptionState", - "ClaimTechDesc", - "ClimbRequirementDesc", - "ClothingDesc", - "CollectibleDesc", - "CombatActionDesc", - "ConstructionRecipeDesc", - "CraftingRecipeDesc", - "DeconstructionRecipeDesc", - "DeployableDesc", - "DimensionDescriptionState", - "ElevatorDesc", - "EmoteDesc", - "EmpireColorDesc", - "EmpireNotificationDesc", - "EmpireRankDesc", - "EmpireSuppliesDesc", - "EmpireTerritoryDesc", - "EnemyAiParamsDesc", - "EnemyDesc", - "EnvironmentDebuffDesc", - "EquipmentDesc", - "ExtractionRecipeDesc", - "FoodDesc", - "GateDesc", - "InteriorInstanceDesc", - "InteriorNetworkDesc", - "InteriorPortalConnectionsDesc", - "InteriorShapeDesc", - "InteriorSpawnDesc", - "ItemConversionRecipeDesc", - "ItemDesc", - "ItemListDesc", - "KnowledgeScrollDesc", - "KnowledgeScrollTypeDesc", - "LootChestDesc", - "LootRarityDesc", - "LootTableDesc", - "NpcDesc", - "OnboardingRewardDesc", - "ParametersDesc", - "PathfindingDesc", - "PavingTileDesc", - "PlayerActionDesc", - "PrivateParametersDesc", - "ResourceClumpDesc", - "ResourceDesc", - "ResourceGrowthRecipeDesc", - "ResourcePlacementRecipeDesc", - "SecondaryKnowledgeDesc", - "SingleResourceToClumpDesc", - "SkillDesc", - "TargetingMatrixDesc", - "TeleportItemDesc", - "TerraformRecipeDesc", - "ToolDesc", - "ToolTypeDesc", - "TravelerTradeOrderDesc", - "WallDesc", - "WeaponDesc", - "WeaponTypeDesc", - ]; - - let state_tables = vec![ - "AIDebugState", - "ActionState", - "ActiveBuffState", - "AdminRestorePlayerStateTimer", - "AlertState", - "AttachedHerdsState", - "AttackOutcomeState", - "AutoClaimState", - "BarterStallState", - "BuildingState", - "CargoState", - "CharacterStatsState", - "ChatMessageState", - "ClaimDescriptionState", - "ClaimRecruitmentState", - "ClaimTechState", - "ClaimTileState", - "CombatState", - "DeployableCollectibleState", - "DeployableState", - "DimensionDescriptionState", - "DimensionNetworkState", - "EmpireChunkState", - "EmpireExpansionState", - "EmpireFoundryState", - "EmpireLogState", - "EmpireNodeSiegeState", - "EmpireNodeState", - "EmpireNotificationState", - "EmpirePlayerDataState", - "EmpirePlayerLogState", - "EmpireRankState", - "EmpireSettlementState", - "EmpireSiegeEngineState", - "EmpireState", - "EnemyMobMonitorState", - "EnemyState", - "EquipmentState", - "ExperienceState", - "ExplorationChunksState", - "FootprintTileState", - "GlobalSearchState", - "GrowthState", - "HealthState", - "HerdState", - "InteriorCollapseTriggerState", - "InventoryState", - "ItemPileState", - "KnowledgeAchievementState", - "KnowledgeBattleActionState", - "KnowledgeBuildingState", - "KnowledgeCargoState", - "KnowledgeConstructionState", - "KnowledgeCraftState", - "KnowledgeDeployableState", - "KnowledgeEnemyState", - "KnowledgeExtractState", - "KnowledgeItemState", - "KnowledgeLoreState", - "KnowledgeNpcState", - "KnowledgePavingState", - "KnowledgeResourcePlacementState", - "KnowledgeResourceState", - "KnowledgeRuinsState", - "KnowledgeSecondaryState", - "KnowledgeVaultState", - "LightSourceState", - "LocationState", - "LootChestState", - "MobileEntityState", - "MountingState", - "MoveValidationStrikeCounterState", - "NpcState", - "OnboardingState", - "PassiveCraftState", - "PavedTileState", - "PlayerActionState", - "PlayerLowercaseUsernameState", - "PlayerNoteState", - "PlayerPrefsState", - "PlayerState", - "PlayerTimestampState", - "PlayerUsernameState", - "PlayerVoteState", - "PortalState", - "ProgressiveActionState", - "ProjectSiteState", - "RentState", - "ResourceState", - "SatiationState", - "SignedInPlayerState", - "SignedInUserState", - "StaminaState", - "StarvingPlayerState", - "TargetState", - "TargetableState", - "TerraformProgressState", - "TerrainChunkState", - "ThreatState", - "ToolbarState", - "TradeOrderState", - "TradeSessionState", - "UnclaimedCollectiblesState", - "UnclaimedShardsState", - "UserModerationState", - "UserSignInState", - "UserState", - "VaultState", - ]; - - let rest_tables = vec![ - "AdminBroadcast", - "AttackImpactTimer", - "AttackTimer", - "AutoLogoutLoopTimer", - "BuildingDecayLoopTimer", - "BuildingDespawnTimer", - "CargoDespawnTimer", - "CargoSpawnTimer", - "ChatCache", - "ClaimTechUnlockTimer", - "ClaimTileCost", - "CollectStatsTimer", - "Config", - "DayNightLoopTimer", - "DeployableDismountTimer", - "DestroyDimensionNetworkTimer", - "EmpireCraftSuppliesTimer", - "EmpireDecayLoopTimer", - "EmpireSiegeLoopTimer", - "EndGracePeriodTimer", - "EnemyDespawnTimer", - "EnemyRegenLoopTimer", - "EnvironmentDebuffLoopTimer", - "ForceGenerateTypes", - "Globals", - "GlobalsAppeared", - "GrowthLoopTimer", - "HideDeployableTimer", - "IdentityRole", - "InteriorSetCollapsedTimer", - "ItemPileDespawnTimer", - "LocationCache", - "LootChestDespawnTimer", - "LootChestSpawnTimer", - "NpcAiLoopTimer", - "PassiveCraftTimer", - "PlayerDeathTimer", - "PlayerRegenLoopTimer", - "PlayerRespawnAfterDeathTimer", - "PlayerUseElevatorTimer", - "PlayerVoteConcludeTimer", - "RentCollectorLoopTimer", - "RentEvictTimer", - "ResetChunkIndexTimer", - "ResetMobileEntityTimer", - "ResourceCount", - "ResourceSpawnTimer", - "ResourcesLog", - "ResourcesRegenLoopTimer", - "RespawnResourceInChunkTimer", - "ServerIdentity", - "SingleResourceClumpInfo", - "StagedStaticData", - "StarvingLoopTimer", - "TeleportPlayerTimer", - "TradeSessionLoopTimer", - ]; - - for table in desc_tables { - let desc_result = download_all_table( - client, - domain, - protocol, - database, - table, - storage_path, - "desc", - ) - .await; - - if let Err(error) = desc_result { - error!("Error while downloading desc table: {error}"); - } - } - - for table in state_tables { - let state_result = download_all_table( - client, - domain, - protocol, - database, - table, - storage_path, - "state", - ) - .await; - - if let Err(error) = state_result { - error!("Error while downloading state table: {error}"); - } - } - - for table in rest_tables { - let rest_result = download_all_table( - client, - domain, - protocol, - database, - table, - storage_path, - "rest", - ) - .await; - - if let Err(error) = rest_result { - error!("Error while downloading rest table: {error}"); - } - } -} - -/// -/// Donwload the table and save it to the storage path with the type as the folder before the name -pub async fn download_all_table( - client: &Client, - domain: &str, - protocol: &str, - database: &str, - table: &str, - storage_path: &Path, - folder: &str, -) -> anyhow::Result<()> { - let response = client - .post(format!("{protocol}{domain}/database/sql/{database}")) - .body(format!("SELECT * FROM {table}")) - .send() - .await; - - let json = match response { - Ok(response) => { - if !response.status().is_success() { - let error = response.text().await?; - error!("Error: {error}"); - return Err(anyhow::anyhow!("Error: {error}")); - } - - response.text().await? - } - Err(error) => { - error!("Error: {error}"); - return Err(anyhow::anyhow!("Error: {error}")); - } - }; - - let folder_to_create = storage_path.join(folder); - if !folder_to_create.exists() { - std::fs::create_dir_all(&folder_to_create)?; - } - let path = storage_path.join(format!("{folder}/{table}.json")); - let mut file = File::create(&path)?; - - println!("Saving to {path:?}"); - - file.write_all(json.as_bytes())?; - - Ok(()) -} - #[derive(Parser, Debug)] #[command(version, author)] pub struct Cli { @@ -1337,6 +968,10 @@ you should provide the directory of that submodule.", #[arg(long, help = "Storage path")] storage_path: Option, }, + Download { + #[command(subcommand)] + command: crate::download::DownloadSubcommand, + }, } pub async fn main() -> anyhow::Result<()> { @@ -1354,6 +989,7 @@ pub async fn main() -> anyhow::Result<()> { match &cli.command { Commands::Migrate { .. } => {} + Commands::Download { .. } => {} Commands::Serve { port, host, @@ -1423,6 +1059,17 @@ pub async fn main() -> anyhow::Result<()> { error!("Error: {err}"); } } + Commands::Download { command } => { + let client = create_default_client(config.clone()); + + crate::download::download_all_tables( + command, + &client, + Path::new(&config.storage_path.clone()), + &config, + ) + .await; + } }; Ok(())