Add multi-database data layer

Introduce aether-data-schema and driver-specific schema generation for Postgres, MySQL, and SQLite.

Split data backends, lifecycle, repositories, and gateway runtime integration across database drivers.

Verified with cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace.
This commit is contained in:
fawney19
2026-05-05 18:27:36 +08:00
parent 099653f732
commit fce7e959e5
372 changed files with 86217 additions and 21160 deletions

View File

@@ -102,6 +102,29 @@ pub(crate) fn encrypt_catalog_secret_with_fallbacks(
encrypt_python_fernet_plaintext(encryption_key.as_ref(), plaintext).ok()
}
pub(crate) fn take_secret_prefix(value: &str, prefix_chars: usize) -> &str {
let end = value
.char_indices()
.nth(prefix_chars)
.map(|(index, _)| index)
.unwrap_or(value.len());
&value[..end]
}
pub(crate) fn take_secret_suffix(value: &str, suffix_chars: usize) -> &str {
if suffix_chars == 0 {
return &value[value.len()..];
}
let start = value
.char_indices()
.rev()
.nth(suffix_chars - 1)
.map(|(index, _)| index)
.unwrap_or(0);
&value[start..]
}
pub(crate) fn masked_catalog_api_key(state: &AppState, key: &StoredProviderCatalogKey) -> String {
match key.auth_type.trim() {
"service_account" | "vertex_ai" => "[Service Account]".to_string(),
@@ -117,13 +140,13 @@ pub(crate) fn masked_catalog_api_key(state: &AppState, key: &StoredProviderCatal
};
decrypt_catalog_secret_with_fallbacks(state.encryption_key(), ciphertext)
.map(|value| {
if value.len() <= 12 {
if value.chars().count() <= 12 {
format!("{value}***")
} else {
format!(
"{}***{}",
&value[..8],
&value[value.len().saturating_sub(4)..]
take_secret_prefix(&value, 8),
take_secret_suffix(&value, 4)
)
}
})
@@ -1621,6 +1644,39 @@ mod tests {
.expect("key transport should build")
}
#[test]
fn masked_catalog_api_key_handles_unicode_plaintext_without_panicking() {
let state = AppState::new().expect("gateway should build");
let encrypted_api_key =
encrypt_python_fernet_plaintext(DEVELOPMENT_ENCRYPTION_KEY, "测试-密钥-1234567890")
.expect("api key ciphertext should build");
let key = StoredProviderCatalogKey::new(
"key-unicode".to_string(),
"provider-test".to_string(),
"default".to_string(),
"api_key".to_string(),
None,
true,
)
.expect("key should build")
.with_transport_fields(
Some(json!(["openai:chat"])),
encrypted_api_key,
None,
None,
None,
None,
None,
None,
None,
)
.expect("key transport should build");
let masked = masked_catalog_api_key(&state, &key);
assert!(masked.contains("***"));
assert_ne!(masked, "***ERROR***");
}
#[test]
fn provider_key_status_snapshot_payload_backfills_missing_quota_from_upstream_metadata() {
let mut key = sample_catalog_key();

View File

@@ -25,7 +25,7 @@ pub(crate) use self::catalog::{
encrypt_catalog_secret_with_fallbacks, masked_catalog_api_key, parse_catalog_auth_config_json,
provider_catalog_key_supports_format, provider_key_health_summary,
provider_key_status_snapshot_payload, sync_provider_key_oauth_status_snapshot,
sync_provider_key_quota_status_snapshot,
sync_provider_key_quota_status_snapshot, take_secret_prefix, take_secret_suffix,
};
pub(crate) use self::email_templates::{
admin_email_template_definition, admin_email_template_html_key,