use crate::connection::{AppState, PoolKind}; use crate::db::redis_driver::{ self, RedisCollectionPage, RedisCommandResult, RedisConnection, RedisDatabaseInfo, RedisScanResult, RedisStreamConsumer, RedisStreamGroup, RedisStreamPage, RedisStreamPendingPage, RedisValue, }; async fn ensure_redis_pool(state: &AppState, connection_id: &str) -> Result<(), String> { state.get_or_create_pool(connection_id, None).await.map(|_| ()) } pub async fn redis_list_databases_core( state: &AppState, connection_id: &str, ) -> Result, String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::list_databases(&mut *con).await } RedisConnection::Cluster(cluster) => redis_driver::list_cluster_databases(cluster).await, }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_scan_keys_core( state: &AppState, connection_id: &str, db: u32, cursor: u64, pattern: &str, count: usize, ) -> Result { redis_scan_keys_batch_core(state, connection_id, db, cursor, pattern, count, 1, true).await } /// Batch-scan keys with server-side multi-SCAN support. /// /// Performs up to `max_iterations` SCAN cycles server-side in a single API /// call, dramatically reducing frontend↔backend roundtrips when fetching many /// keys (e.g. "fetch all" in the key browser). TYPE and TTL metadata is optional. pub async fn redis_scan_keys_batch_core( state: &AppState, connection_id: &str, db: u32, cursor: u64, pattern: &str, count: usize, max_iterations: usize, include_types: bool, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::scan_keys_batch(&mut *con, cursor, pattern, count, max_iterations, include_types).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; redis_driver::scan_cluster_keys_batch(cluster, cursor, pattern, count, max_iterations, include_types) .await } }, _ => Err("Not a Redis connection".to_string()), } } #[allow(clippy::too_many_arguments)] pub async fn redis_scan_values_core( state: &AppState, connection_id: &str, db: u32, cursor: u64, pattern: &str, query: &str, include_key_matches: bool, count: usize, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::scan_values_page(&mut *con, cursor, pattern, query, include_key_matches, count).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; redis_driver::scan_cluster_values_page(cluster, cursor, pattern, query, include_key_matches, count) .await } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_get_value_core(state: &AppState, connection_id: &str, key: &str) -> Result { redis_get_value_in_db_core(state, connection_id, 0, key).await } pub async fn redis_get_value_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_value(&mut *con, &key).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_value(&mut con, &key).await } } } _ => Err("Not a Redis connection".to_string()), } } /// Fetch only the TTL for a selected key. This avoids repeatedly loading a /// potentially large Redis value when the desktop detail view is polling. pub async fn redis_get_ttl_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_ttl(&mut *con, &key).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_ttl(&mut con, &key).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_stream_entries_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, cursor: Option<&str>, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_stream_entries_page(&mut *con, &key, cursor).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_stream_entries_page(&mut con, &key, cursor).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_stream_groups_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, ) -> Result, String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_stream_groups(&mut *con, &key).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_stream_groups(&mut con, &key).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_stream_consumers_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, group_raw: &str, ) -> Result, String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; let group = redis_driver::redis_key_raw_to_bytes(group_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_stream_consumers(&mut *con, &key, &group).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_stream_consumers(&mut con, &key, &group).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_stream_pending_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, group_raw: &str, cursor: Option<&str>, consumer_raw: Option<&str>, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; let group = redis_driver::redis_key_raw_to_bytes(group_raw)?; let consumer = consumer_raw.map(redis_driver::redis_key_raw_to_bytes).transpose()?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::get_stream_pending_page(&mut *con, &key, &group, cursor, consumer.as_deref()).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::get_stream_pending_page(&mut con, &key, &group, cursor, consumer.as_deref()).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_set_string_core( state: &AppState, connection_id: &str, key: &str, value: &str, ttl: Option, ) -> Result<(), String> { redis_set_string_in_db_core(state, connection_id, 0, key, value, ttl).await } pub async fn redis_set_string_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, value: &str, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_string(&mut *con, &key, value, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_string(&mut con, &key, value, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_delete_key_core(state: &AppState, connection_id: &str, key: &str) -> Result<(), String> { redis_delete_key_in_db_core(state, connection_id, 0, key).await } pub async fn redis_delete_key_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::delete_key(&mut *con, &key).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::delete_key(&mut con, &key).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_rename_key_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, new_key_raw: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Connection not found")?; let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; let new_key = redis_driver::redis_key_raw_to_bytes(new_key_raw)?; if key == new_key { return Ok(()); } match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::rename_key(&mut *con, &key, &new_key).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::rename_key(&mut con, &key, &new_key).await } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_hash_set_core( state: &AppState, connection_id: &str, key: &str, field: &str, value: &str, ttl: Option, ) -> Result<(), String> { redis_hash_set_in_db_core(state, connection_id, 0, key, field, value, ttl).await } pub async fn redis_hash_set_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, field: &str, value: &str, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::hash_set(&mut *con, &key, field, value, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::hash_set(&mut con, &key, field, value, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_hash_del_core(state: &AppState, connection_id: &str, key: &str, field: &str) -> Result<(), String> { redis_hash_del_in_db_core(state, connection_id, 0, key, field).await } pub async fn redis_hash_del_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, field: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::hash_del(&mut *con, &key, field).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::hash_del(&mut con, &key, field).await } } } _ => Err("Not a Redis connection".to_string()), } } /// Atomically update a Redis hash field and optionally rename it. /// /// The driver executes the source check, destination collision check, value /// write, and old-field deletion in one EVAL on the connection for the hash /// key. Keeping the dispatch here mirrors the other Redis operations so /// standalone and cluster connections follow the same path. pub async fn redis_hash_field_update_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, old_field: &str, new_field: &str, value: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::hash_field_update(&mut *con, &key, old_field, new_field, value).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::hash_field_update(&mut con, &key, old_field, new_field, value).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_hash_field_set_ttl_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, field: &str, ttl: i64, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_hash_field_ttl(&mut *con, &key, field, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_hash_field_ttl(&mut con, &key, field, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_hash_field_set_expire_at_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, field: &str, expire_at: i64, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_hash_field_expire_at(&mut *con, &key, field, expire_at).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_hash_field_expire_at(&mut con, &key, field, expire_at).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_list_push_core( state: &AppState, connection_id: &str, key: &str, value: &str, ttl: Option, ) -> Result<(), String> { redis_list_push_in_db_core(state, connection_id, 0, key, value, ttl).await } pub async fn redis_list_push_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, value: &str, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::list_push(&mut *con, &key, value, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::list_push(&mut con, &key, value, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_list_set_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, index: i64, value: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::list_set(&mut *con, &key, index, value).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::list_set(&mut con, &key, index, value).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_list_remove_core( state: &AppState, connection_id: &str, key: &str, index: i64, ) -> Result<(), String> { redis_list_remove_in_db_core(state, connection_id, 0, key, index).await } pub async fn redis_list_remove_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, index: i64, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::list_remove(&mut *con, &key, index).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::list_remove(&mut con, &key, index).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_set_add_core( state: &AppState, connection_id: &str, key: &str, member: &str, ttl: Option, ) -> Result<(), String> { redis_set_add_in_db_core(state, connection_id, 0, key, member, ttl).await } pub async fn redis_set_add_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, member: &str, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_add(&mut *con, &key, member, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_add(&mut con, &key, member, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_set_remove_core( state: &AppState, connection_id: &str, key: &str, member: &str, ) -> Result<(), String> { redis_set_remove_in_db_core(state, connection_id, 0, key, member).await } pub async fn redis_set_remove_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, member: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_remove(&mut *con, &key, member).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_remove(&mut con, &key, member).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_zadd_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, member: &str, score: f64, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::zadd(&mut *con, &key, member, score, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::zadd(&mut con, &key, member, score, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_zrem_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, member: &str, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::zrem(&mut *con, &key, member).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::zrem(&mut con, &key, member).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_zset_update_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, original_member: &str, expected_score: &str, member: &str, score: &str, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::zset_update(&mut *con, &key, original_member, expected_score, member, score).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::zset_update(&mut con, &key, original_member, expected_score, member, score).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_stream_add_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, entry_id: &str, fields: Vec<(String, String)>, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::stream_add(&mut *con, &key, entry_id, &fields, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::stream_add(&mut con, &key, entry_id, &fields, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_json_set_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, value: &str, ttl: Option, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::json_set(&mut *con, &key, value, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::json_set(&mut con, &key, value, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_check_json_module_in_db_core( state: &AppState, connection_id: &str, db: u32, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::check_json_module(&mut *con).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_any_connection(cluster).await?; redis_driver::check_json_module(&mut con).await } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_set_ttl_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, ttl: i64, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_ttl(&mut *con, &key, ttl).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_ttl(&mut con, &key, ttl).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_set_expire_at_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, expire_at: i64, ) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::set_expire_at(&mut *con, &key, expire_at).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::set_expire_at(&mut con, &key, expire_at).await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_delete_keys_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raws: &[String], ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let keys: Result>, String> = key_raws.iter().map(|k| redis_driver::redis_key_raw_to_bytes(k)).collect(); let keys = keys?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::delete_keys(&mut *con, &keys).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut deleted = 0; for key in &keys { let mut con = redis_driver::cluster_key_connection(cluster, key).await?; deleted += redis_driver::delete_keys(&mut con, std::slice::from_ref(key)).await?; } Ok(deleted) } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_flush_db_core(state: &AppState, connection_id: &str, db: u32) -> Result<(), String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::flush_db(&mut *con).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; redis_driver::flush_cluster(cluster).await } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_execute_command_core( state: &AppState, connection_id: &str, db: u32, command: &str, skip_safety_check: bool, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::execute_console_command(&mut *con, db, command, skip_safety_check).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = if let Ok(argv) = redis_driver::parse_command_argv(command) { if let Some(command_name) = argv.first() { if command_name.eq_ignore_ascii_case("SELECT") { return Err("Redis Cluster only supports db0; SELECT is not available".to_string()); } } if command_may_target_first_key(&argv) { redis_driver::cluster_key_connection(cluster, argv[1].as_bytes()).await? } else { redis_driver::cluster_any_connection(cluster).await? } } else { redis_driver::cluster_any_connection(cluster).await? }; redis_driver::execute_command(&mut con, command, skip_safety_check).await } }, _ => Err("Not a Redis connection".to_string()), } } fn command_may_target_first_key(argv: &[String]) -> bool { if argv.len() < 2 { return false; } !matches!( argv[0].to_ascii_uppercase().as_str(), "PING" | "INFO" | "DBSIZE" | "TIME" | "ROLE" | "CLUSTER" | "CLIENT" | "COMMAND" | "HELLO" | "AUTH" | "QUIT" ) } pub async fn redis_load_more_in_db_core( state: &AppState, connection_id: &str, db: u32, key_raw: &str, key_type: &str, cursor: u64, count: usize, filter: Option<&str>, sort_direction: Option<&str>, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => { let key = redis_driver::redis_key_raw_to_bytes(key_raw)?; match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::load_more_collection(&mut *con, &key, key_type, cursor, count, filter, sort_direction) .await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_key_connection(cluster, &key).await?; redis_driver::load_more_collection(&mut con, &key, key_type, cursor, count, filter, sort_direction) .await } } } _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_publish_core( state: &AppState, connection_id: &str, db: u32, channel: &str, message: &str, ) -> Result { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; redis_driver::select_db(&mut *con, db).await?; redis_driver::publish_message(&mut *con, channel, message).await } RedisConnection::Cluster(cluster) => { redis_driver::ensure_cluster_db(db)?; let mut con = redis_driver::cluster_any_connection(cluster).await?; redis_driver::publish_message(&mut con, channel, message).await } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_create_monitor_core(state: &AppState, connection_id: &str) -> Result { let config = state.configs.read().await.get(connection_id).ok_or("Connection config not found")?.clone(); if config.db_type != crate::models::connection::DatabaseType::Redis { return Err("Not a Redis connection".to_string()); } if config.uses_redis_sentinel() || config.uses_redis_cluster() { return Err( "MONITOR requires a direct Redis node connection; connect to the node you want to monitor".to_string() ); } let (host, port) = state.connection_host_port(connection_id, &config).await?; let mut runtime_config = config.clone(); state.apply_session_credential(&config, &mut runtime_config, connection_id); redis_driver::connect_monitor( &runtime_config, &host, port, std::time::Duration::from_secs(config.effective_connect_timeout_secs()), ) .await } pub async fn redis_create_pubsub_core(state: &AppState, connection_id: &str) -> Result { let configs = state.configs.read().await; let config = configs.get(connection_id).ok_or("Connection config not found")?.clone(); drop(configs); if config.db_type != crate::models::connection::DatabaseType::Redis { return Err("Not a Redis connection".to_string()); } let (host, port) = state.connection_host_port(connection_id, &config).await?; let timeout = std::time::Duration::from_secs(config.effective_connect_timeout_secs()); redis_driver::connect_pubsub(&config, &host, port, timeout).await } pub async fn redis_slowlog_get_core( state: &AppState, connection_id: &str, count: usize, node_host: Option, node_port: Option, ) -> Result, String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Direct(con) => { let mut con = con.lock().await; // SLOWLOG is a server-level command, no select_db needed redis_driver::get_slowlog(&mut *con, count).await } RedisConnection::Cluster(cluster) => { if let (Some(host), Some(port)) = (node_host.as_ref(), node_port) { let endpoint = redis_driver::RedisNodeEndpoint { host: host.clone(), port }; let mut con = redis_driver::connect_cluster_node(cluster, &endpoint).await?; redis_driver::get_slowlog(&mut con, count).await } else { // No node specified — return empty (frontend enforces selection) Ok(Vec::new()) } } }, _ => Err("Not a Redis connection".to_string()), } } pub async fn redis_cluster_master_nodes_core( state: &AppState, connection_id: &str, ) -> Result, String> { ensure_redis_pool(state, connection_id).await?; let pool = state.pool_handle(connection_id).await.ok_or("Not found")?; match &pool { PoolKind::Redis(redis) => match redis.as_ref() { RedisConnection::Cluster(cluster) => redis_driver::cluster_master_nodes(cluster).await, _ => Ok(Vec::new()), }, _ => Err("Not a Redis connection".to_string()), } }