mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
refactor: extract runtime state backends
This commit is contained in:
@@ -77,16 +77,21 @@ impl AppState {
|
||||
value: &str,
|
||||
ttl_seconds: u64,
|
||||
) -> Result<(), GatewayError> {
|
||||
self.data
|
||||
.cache_set_string_with_ttl(key, value, ttl_seconds)
|
||||
self.runtime_state
|
||||
.kv_set(
|
||||
key,
|
||||
value.to_string(),
|
||||
Some(std::time::Duration::from_secs(ttl_seconds)),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn cache_delete_key(&self, key: &str) -> Result<(), GatewayError> {
|
||||
self.data
|
||||
.cache_delete_key(key)
|
||||
self.runtime_state
|
||||
.kv_delete(key)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ impl AppState {
|
||||
}
|
||||
|
||||
pub fn has_usage_worker_backend(&self) -> bool {
|
||||
self.data.has_usage_worker_runner()
|
||||
self.data.has_usage_worker_queue()
|
||||
}
|
||||
|
||||
pub fn has_wallet_data_reader(&self) -> bool {
|
||||
|
||||
@@ -10,41 +10,16 @@ impl AppState {
|
||||
) -> Result<bool, GatewayError> {
|
||||
const ADMIN_SECURITY_BLACKLIST_PREFIX: &str = "ip:blacklist:";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
let key = runner
|
||||
.keyspace()
|
||||
.key(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}{ip_address}"));
|
||||
let result = if let Some(ttl_seconds) = ttl_seconds {
|
||||
redis::cmd("SETEX")
|
||||
.arg(&key)
|
||||
.arg(ttl_seconds)
|
||||
.arg(reason)
|
||||
.query_async::<String>(&mut connection)
|
||||
.await
|
||||
} else {
|
||||
redis::cmd("SET")
|
||||
.arg(&key)
|
||||
.arg(reason)
|
||||
.query_async::<String>(&mut connection)
|
||||
.await
|
||||
};
|
||||
return Ok(result.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_blacklist_store.as_ref() {
|
||||
store
|
||||
.lock()
|
||||
.expect("admin security blacklist store should lock")
|
||||
.insert(ip_address.to_string(), reason.to_string());
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
let key = format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}{ip_address}");
|
||||
self.runtime_state
|
||||
.kv_set(
|
||||
&key,
|
||||
reason.to_string(),
|
||||
ttl_seconds.map(std::time::Duration::from_secs),
|
||||
)
|
||||
.await
|
||||
.map(|_| true)
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_admin_security_blacklist(
|
||||
@@ -53,36 +28,11 @@ impl AppState {
|
||||
) -> Result<bool, GatewayError> {
|
||||
const ADMIN_SECURITY_BLACKLIST_PREFIX: &str = "ip:blacklist:";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
let key = runner
|
||||
.keyspace()
|
||||
.key(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}{ip_address}"));
|
||||
let deleted = match redis::cmd("DEL")
|
||||
.arg(&key)
|
||||
.query_async::<i64>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
return Ok(deleted > 0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_blacklist_store.as_ref() {
|
||||
let removed = store
|
||||
.lock()
|
||||
.expect("admin security blacklist store should lock")
|
||||
.remove(ip_address)
|
||||
.is_some();
|
||||
return Ok(removed);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
let key = format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}{ip_address}");
|
||||
self.runtime_state
|
||||
.kv_delete(&key)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn admin_security_blacklist_stats(
|
||||
@@ -90,48 +40,13 @@ impl AppState {
|
||||
) -> Result<(bool, usize, Option<String>), GatewayError> {
|
||||
const ADMIN_SECURITY_BLACKLIST_PREFIX: &str = "ip:blacklist:";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok((false, 0, Some("Redis 不可用".to_string()))),
|
||||
};
|
||||
let pattern = runner
|
||||
.keyspace()
|
||||
.key(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}*"));
|
||||
let mut cursor = 0u64;
|
||||
let mut total = 0usize;
|
||||
loop {
|
||||
let (next_cursor, keys) = match redis::cmd("SCAN")
|
||||
.arg(cursor)
|
||||
.arg("MATCH")
|
||||
.arg(&pattern)
|
||||
.arg("COUNT")
|
||||
.arg(100)
|
||||
.query_async::<(u64, Vec<String>)>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(err) => return Ok((false, 0, Some(err.to_string()))),
|
||||
};
|
||||
total += keys.len();
|
||||
if next_cursor == 0 {
|
||||
break;
|
||||
}
|
||||
cursor = next_cursor;
|
||||
}
|
||||
return Ok((true, total, None));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_blacklist_store.as_ref() {
|
||||
let total = store
|
||||
.lock()
|
||||
.expect("admin security blacklist store should lock")
|
||||
.len();
|
||||
return Ok((true, total, None));
|
||||
}
|
||||
|
||||
Ok((false, 0, Some("Redis 不可用".to_string())))
|
||||
let total = self
|
||||
.runtime_state
|
||||
.scan_keys(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}*"), 100)
|
||||
.await
|
||||
.map(|keys| keys.len())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
||||
Ok((true, total, None))
|
||||
}
|
||||
|
||||
pub(crate) async fn list_admin_security_blacklist(
|
||||
@@ -139,83 +54,40 @@ impl AppState {
|
||||
) -> Result<Vec<AdminSecurityBlacklistEntry>, GatewayError> {
|
||||
const ADMIN_SECURITY_BLACKLIST_PREFIX: &str = "ip:blacklist:";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(Vec::new()),
|
||||
let keys = self
|
||||
.runtime_state
|
||||
.scan_keys(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}*"), 100)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
||||
let mut entries = Vec::new();
|
||||
for full_key in keys {
|
||||
let raw_key = self.runtime_state.strip_namespace(&full_key);
|
||||
let ip_address = raw_key
|
||||
.strip_prefix(ADMIN_SECURITY_BLACKLIST_PREFIX)
|
||||
.unwrap_or(raw_key)
|
||||
.to_string();
|
||||
let Some(reason) = self
|
||||
.runtime_state
|
||||
.kv_get(raw_key)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let pattern = runner
|
||||
.keyspace()
|
||||
.key(&format!("{ADMIN_SECURITY_BLACKLIST_PREFIX}*"));
|
||||
let prefix = runner.keyspace().key(ADMIN_SECURITY_BLACKLIST_PREFIX);
|
||||
let mut cursor = 0u64;
|
||||
let mut entries = Vec::new();
|
||||
loop {
|
||||
let (next_cursor, keys) = match redis::cmd("SCAN")
|
||||
.arg(cursor)
|
||||
.arg("MATCH")
|
||||
.arg(&pattern)
|
||||
.arg("COUNT")
|
||||
.arg(100)
|
||||
.query_async::<(u64, Vec<String>)>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(_) => break,
|
||||
};
|
||||
for full_key in keys {
|
||||
let ip_address = full_key
|
||||
.strip_prefix(prefix.as_str())
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or_else(|| full_key.clone());
|
||||
let reason: Result<String, _> = redis::cmd("GET")
|
||||
.arg(&full_key)
|
||||
.query_async(&mut connection)
|
||||
.await;
|
||||
let reason = match reason {
|
||||
Ok(value) => value,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let ttl = match redis::cmd("TTL")
|
||||
.arg(&full_key)
|
||||
.query_async::<i64>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) if value >= 0 => Some(value),
|
||||
_ => None,
|
||||
};
|
||||
entries.push(AdminSecurityBlacklistEntry {
|
||||
ip_address,
|
||||
reason,
|
||||
ttl_seconds: ttl,
|
||||
});
|
||||
}
|
||||
if next_cursor == 0 {
|
||||
break;
|
||||
}
|
||||
cursor = next_cursor;
|
||||
}
|
||||
entries.sort_by(|a, b| a.ip_address.cmp(&b.ip_address));
|
||||
return Ok(entries);
|
||||
let ttl_seconds = self
|
||||
.runtime_state
|
||||
.kv_ttl_seconds(raw_key)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?
|
||||
.filter(|ttl| *ttl >= 0);
|
||||
entries.push(AdminSecurityBlacklistEntry {
|
||||
ip_address,
|
||||
reason,
|
||||
ttl_seconds,
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_blacklist_store.as_ref() {
|
||||
let mut entries = store
|
||||
.lock()
|
||||
.expect("admin security blacklist store should lock")
|
||||
.iter()
|
||||
.map(|(ip, reason)| AdminSecurityBlacklistEntry {
|
||||
ip_address: ip.clone(),
|
||||
reason: reason.clone(),
|
||||
ttl_seconds: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_by(|a, b| a.ip_address.cmp(&b.ip_address));
|
||||
return Ok(entries);
|
||||
}
|
||||
|
||||
Ok(Vec::new())
|
||||
entries.sort_by(|a, b| a.ip_address.cmp(&b.ip_address));
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
pub(crate) async fn add_admin_security_whitelist(
|
||||
@@ -224,34 +96,11 @@ impl AppState {
|
||||
) -> Result<bool, GatewayError> {
|
||||
const ADMIN_SECURITY_WHITELIST_KEY: &str = "ip:whitelist";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
let key = runner.keyspace().key(ADMIN_SECURITY_WHITELIST_KEY);
|
||||
let added = match redis::cmd("SADD")
|
||||
.arg(&key)
|
||||
.arg(ip_address)
|
||||
.query_async::<i64>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
return Ok(added >= 0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_whitelist_store.as_ref() {
|
||||
store
|
||||
.lock()
|
||||
.expect("admin security whitelist store should lock")
|
||||
.insert(ip_address.to_string());
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
self.runtime_state
|
||||
.set_add(ADMIN_SECURITY_WHITELIST_KEY, ip_address)
|
||||
.await
|
||||
.map(|_| true)
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_admin_security_whitelist(
|
||||
@@ -260,67 +109,18 @@ impl AppState {
|
||||
) -> Result<bool, GatewayError> {
|
||||
const ADMIN_SECURITY_WHITELIST_KEY: &str = "ip:whitelist";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
let key = runner.keyspace().key(ADMIN_SECURITY_WHITELIST_KEY);
|
||||
let removed = match redis::cmd("SREM")
|
||||
.arg(&key)
|
||||
.arg(ip_address)
|
||||
.query_async::<i64>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
return Ok(removed > 0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_whitelist_store.as_ref() {
|
||||
let removed = store
|
||||
.lock()
|
||||
.expect("admin security whitelist store should lock")
|
||||
.remove(ip_address);
|
||||
return Ok(removed);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
self.runtime_state
|
||||
.set_remove(ADMIN_SECURITY_WHITELIST_KEY, ip_address)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn list_admin_security_whitelist(&self) -> Result<Vec<String>, GatewayError> {
|
||||
const ADMIN_SECURITY_WHITELIST_KEY: &str = "ip:whitelist";
|
||||
|
||||
if let Some(runner) = self.redis_kv_runner() {
|
||||
let mut connection = match runner.client().get_multiplexed_async_connection().await {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(Vec::new()),
|
||||
};
|
||||
let key = runner.keyspace().key(ADMIN_SECURITY_WHITELIST_KEY);
|
||||
let mut whitelist = match redis::cmd("SMEMBERS")
|
||||
.arg(&key)
|
||||
.query_async::<Vec<String>>(&mut connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok(Vec::new()),
|
||||
};
|
||||
whitelist.sort();
|
||||
return Ok(whitelist);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_security_whitelist_store.as_ref() {
|
||||
return Ok(store
|
||||
.lock()
|
||||
.expect("admin security whitelist store should lock")
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect());
|
||||
}
|
||||
|
||||
Ok(Vec::new())
|
||||
self.runtime_state
|
||||
.set_members(ADMIN_SECURITY_WHITELIST_KEY)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user