mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate
- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦 - 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块 - 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支 - 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合 - 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor - 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
pub(crate) use aether_data::repository::billing::{
|
||||
AdminBillingCollectorRecord, AdminBillingCollectorWriteInput, AdminBillingPresetApplyResult,
|
||||
AdminBillingRuleRecord, AdminBillingRuleWriteInput,
|
||||
};
|
||||
pub(crate) use aether_data::repository::system::AdminSecurityBlacklistEntry;
|
||||
pub(crate) use aether_data::repository::wallet::{
|
||||
AdminPaymentCallbackRecord, AdminWalletPaymentOrderRecord, AdminWalletRefundRecord,
|
||||
AdminWalletTransactionRecord,
|
||||
};
|
||||
pub(crate) use aether_data_contracts::repository::billing::{
|
||||
AdminBillingCollectorRecord, AdminBillingCollectorWriteInput, AdminBillingPresetApplyResult,
|
||||
AdminBillingRuleRecord, AdminBillingRuleWriteInput,
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@ use std::sync::Mutex as StdMutex;
|
||||
use aether_runtime::{ConcurrencyGate, DistributedConcurrencyGate};
|
||||
|
||||
use super::super::async_task::{VideoTaskPollerConfig, VideoTaskService};
|
||||
use super::super::fallback_metrics;
|
||||
use super::super::cache::{
|
||||
AuthApiKeyLastUsedCache, AuthContextCache, DirectPlanBypassCache, SchedulerAffinityCache,
|
||||
};
|
||||
use super::super::data::GatewayDataState;
|
||||
use super::super::fallback_metrics;
|
||||
use super::super::rate_limit::FrontdoorUserRpmLimiter;
|
||||
use super::super::{provider_transport, usage};
|
||||
use super::{
|
||||
@@ -45,26 +45,18 @@ pub struct AppState {
|
||||
pub(crate) local_execution_runtime_miss_diagnostics:
|
||||
Arc<StdMutex<HashMap<String, LocalExecutionRuntimeMissDiagnostic>>>,
|
||||
pub(crate) admin_monitoring_error_stats_reset_at: Arc<StdMutex<Option<u64>>>,
|
||||
pub(crate) provider_delete_tasks:
|
||||
Arc<StdMutex<HashMap<String, LocalProviderDeleteTaskState>>>,
|
||||
pub(crate) provider_delete_tasks: Arc<StdMutex<HashMap<String, LocalProviderDeleteTaskState>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) provider_oauth_state_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) provider_oauth_state_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) provider_oauth_device_session_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) provider_oauth_device_session_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) provider_oauth_batch_task_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) provider_oauth_batch_task_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) auth_session_store: Option<
|
||||
Arc<
|
||||
StdMutex<HashMap<String, crate::data::state::StoredUserSessionRecord>>,
|
||||
>,
|
||||
>,
|
||||
pub(crate) auth_session_store:
|
||||
Option<Arc<StdMutex<HashMap<String, crate::data::state::StoredUserSessionRecord>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) auth_email_verification_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) auth_email_verification_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) auth_email_delivery_store: Option<Arc<StdMutex<Vec<serde_json::Value>>>>,
|
||||
#[cfg(test)]
|
||||
@@ -97,8 +89,7 @@ pub struct AppState {
|
||||
pub(crate) admin_billing_collector_store:
|
||||
Option<Arc<StdMutex<HashMap<String, AdminBillingCollectorRecord>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) admin_security_blacklist_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) admin_security_blacklist_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) admin_security_whitelist_store:
|
||||
Option<Arc<StdMutex<std::collections::BTreeSet<String>>>>,
|
||||
@@ -106,9 +97,7 @@ pub struct AppState {
|
||||
pub(crate) admin_monitoring_cache_affinity_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) admin_monitoring_redis_key_store:
|
||||
Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
pub(crate) admin_monitoring_redis_key_store: Option<Arc<StdMutex<HashMap<String, String>>>>,
|
||||
#[cfg(test)]
|
||||
pub(crate) provider_oauth_token_url_overrides:
|
||||
Arc<StdMutex<HashMap<String, String>>>,
|
||||
pub(crate) provider_oauth_token_url_overrides: Arc<StdMutex<HashMap<String, String>>>,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::{AppState, GatewayError, LocalMutationOutcome, LocalProviderDeleteTaskState};
|
||||
use aether_data_contracts::repository::{candidates, global_models, provider_catalog};
|
||||
|
||||
impl AppState {
|
||||
pub fn has_provider_catalog_data_reader(&self) -> bool {
|
||||
@@ -32,10 +33,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_catalog_providers(
|
||||
&self,
|
||||
active_only: bool,
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogProvider>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogProvider>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_providers(active_only)
|
||||
.await
|
||||
@@ -45,10 +43,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_catalog_endpoints_by_provider_ids(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogEndpoint>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_endpoints_by_provider_ids(provider_ids)
|
||||
.await
|
||||
@@ -57,9 +52,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_public_global_models(
|
||||
&self,
|
||||
query: &aether_data::repository::global_models::PublicGlobalModelQuery,
|
||||
) -> Result<aether_data::repository::global_models::StoredPublicGlobalModelPage, GatewayError>
|
||||
{
|
||||
query: &global_models::PublicGlobalModelQuery,
|
||||
) -> Result<global_models::StoredPublicGlobalModelPage, GatewayError> {
|
||||
self.data
|
||||
.list_public_global_models(query)
|
||||
.await
|
||||
@@ -158,8 +152,7 @@ impl AppState {
|
||||
pub(crate) async fn get_public_global_model_by_name(
|
||||
&self,
|
||||
model_name: &str,
|
||||
) -> Result<Option<aether_data::repository::global_models::StoredPublicGlobalModel>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<global_models::StoredPublicGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.get_public_global_model_by_name(model_name)
|
||||
.await
|
||||
@@ -168,9 +161,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_public_catalog_models(
|
||||
&self,
|
||||
query: &aether_data::repository::global_models::PublicCatalogModelListQuery,
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredPublicCatalogModel>, GatewayError>
|
||||
{
|
||||
query: &global_models::PublicCatalogModelListQuery,
|
||||
) -> Result<Vec<global_models::StoredPublicCatalogModel>, GatewayError> {
|
||||
self.data
|
||||
.list_public_catalog_models(query)
|
||||
.await
|
||||
@@ -179,9 +171,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn search_public_catalog_models(
|
||||
&self,
|
||||
query: &aether_data::repository::global_models::PublicCatalogModelSearchQuery,
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredPublicCatalogModel>, GatewayError>
|
||||
{
|
||||
query: &global_models::PublicCatalogModelSearchQuery,
|
||||
) -> Result<Vec<global_models::StoredPublicCatalogModel>, GatewayError> {
|
||||
self.data
|
||||
.search_public_catalog_models(query)
|
||||
.await
|
||||
@@ -190,9 +181,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_admin_provider_models(
|
||||
&self,
|
||||
query: &aether_data::repository::global_models::AdminProviderModelListQuery,
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredAdminProviderModel>, GatewayError>
|
||||
{
|
||||
query: &global_models::AdminProviderModelListQuery,
|
||||
) -> Result<Vec<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.list_admin_provider_models(query)
|
||||
.await
|
||||
@@ -201,9 +191,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_admin_global_models(
|
||||
&self,
|
||||
query: &aether_data::repository::global_models::AdminGlobalModelListQuery,
|
||||
) -> Result<aether_data::repository::global_models::StoredAdminGlobalModelPage, GatewayError>
|
||||
{
|
||||
query: &global_models::AdminGlobalModelListQuery,
|
||||
) -> Result<global_models::StoredAdminGlobalModelPage, GatewayError> {
|
||||
self.data
|
||||
.list_admin_global_models(query)
|
||||
.await
|
||||
@@ -214,10 +203,7 @@ impl AppState {
|
||||
&self,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::global_models::StoredAdminProviderModel>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Option<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.get_admin_provider_model(provider_id, model_id)
|
||||
.await
|
||||
@@ -227,8 +213,7 @@ impl AppState {
|
||||
pub(crate) async fn list_admin_provider_available_source_models(
|
||||
&self,
|
||||
provider_id: &str,
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredAdminProviderModel>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.list_admin_provider_available_source_models(provider_id)
|
||||
.await
|
||||
@@ -238,8 +223,7 @@ impl AppState {
|
||||
pub(crate) async fn get_admin_global_model_by_id(
|
||||
&self,
|
||||
global_model_id: &str,
|
||||
) -> Result<Option<aether_data::repository::global_models::StoredAdminGlobalModel>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<global_models::StoredAdminGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.get_admin_global_model_by_id(global_model_id)
|
||||
.await
|
||||
@@ -249,8 +233,7 @@ impl AppState {
|
||||
pub(crate) async fn get_admin_global_model_by_name(
|
||||
&self,
|
||||
model_name: &str,
|
||||
) -> Result<Option<aether_data::repository::global_models::StoredAdminGlobalModel>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<global_models::StoredAdminGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.get_admin_global_model_by_name(model_name)
|
||||
.await
|
||||
@@ -260,8 +243,7 @@ impl AppState {
|
||||
pub(crate) async fn list_admin_provider_models_by_global_model_id(
|
||||
&self,
|
||||
global_model_id: &str,
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredAdminProviderModel>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.list_admin_provider_models_by_global_model_id(global_model_id)
|
||||
.await
|
||||
@@ -270,11 +252,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn create_admin_provider_model(
|
||||
&self,
|
||||
record: &aether_data::repository::global_models::UpsertAdminProviderModelRecord,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::global_models::StoredAdminProviderModel>,
|
||||
GatewayError,
|
||||
> {
|
||||
record: &global_models::UpsertAdminProviderModelRecord,
|
||||
) -> Result<Option<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.create_admin_provider_model(record)
|
||||
.await
|
||||
@@ -283,11 +262,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_admin_provider_model(
|
||||
&self,
|
||||
record: &aether_data::repository::global_models::UpsertAdminProviderModelRecord,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::global_models::StoredAdminProviderModel>,
|
||||
GatewayError,
|
||||
> {
|
||||
record: &global_models::UpsertAdminProviderModelRecord,
|
||||
) -> Result<Option<global_models::StoredAdminProviderModel>, GatewayError> {
|
||||
self.data
|
||||
.update_admin_provider_model(record)
|
||||
.await
|
||||
@@ -307,9 +283,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn create_admin_global_model(
|
||||
&self,
|
||||
record: &aether_data::repository::global_models::CreateAdminGlobalModelRecord,
|
||||
) -> Result<Option<aether_data::repository::global_models::StoredAdminGlobalModel>, GatewayError>
|
||||
{
|
||||
record: &global_models::CreateAdminGlobalModelRecord,
|
||||
) -> Result<Option<global_models::StoredAdminGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.create_admin_global_model(record)
|
||||
.await
|
||||
@@ -318,9 +293,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_admin_global_model(
|
||||
&self,
|
||||
record: &aether_data::repository::global_models::UpdateAdminGlobalModelRecord,
|
||||
) -> Result<Option<aether_data::repository::global_models::StoredAdminGlobalModel>, GatewayError>
|
||||
{
|
||||
record: &global_models::UpdateAdminGlobalModelRecord,
|
||||
) -> Result<Option<global_models::StoredAdminGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.update_admin_global_model(record)
|
||||
.await
|
||||
@@ -340,8 +314,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_model_stats(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<Vec<aether_data::repository::global_models::StoredProviderModelStats>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<global_models::StoredProviderModelStats>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_model_stats(provider_ids)
|
||||
.await
|
||||
@@ -351,10 +324,7 @@ impl AppState {
|
||||
pub(crate) async fn list_active_global_model_ids_by_provider_ids(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::global_models::StoredProviderActiveGlobalModel>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<global_models::StoredProviderActiveGlobalModel>, GatewayError> {
|
||||
self.data
|
||||
.list_active_global_model_ids_by_provider_ids(provider_ids)
|
||||
.await
|
||||
@@ -366,8 +336,7 @@ impl AppState {
|
||||
endpoint_ids: &[String],
|
||||
since_unix_secs: u64,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::candidates::StoredRequestCandidate>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::StoredRequestCandidate>, GatewayError> {
|
||||
self.data
|
||||
.list_finalized_request_candidates_by_endpoint_ids_since(
|
||||
endpoint_ids,
|
||||
@@ -382,8 +351,7 @@ impl AppState {
|
||||
&self,
|
||||
endpoint_ids: &[String],
|
||||
since_unix_secs: u64,
|
||||
) -> Result<Vec<aether_data::repository::candidates::PublicHealthStatusCount>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::PublicHealthStatusCount>, GatewayError> {
|
||||
self.data
|
||||
.count_finalized_request_candidate_statuses_by_endpoint_ids_since(
|
||||
endpoint_ids,
|
||||
@@ -399,8 +367,7 @@ impl AppState {
|
||||
since_unix_secs: u64,
|
||||
until_unix_secs: u64,
|
||||
segments: u32,
|
||||
) -> Result<Vec<aether_data::repository::candidates::PublicHealthTimelineBucket>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::PublicHealthTimelineBucket>, GatewayError> {
|
||||
self.data
|
||||
.aggregate_finalized_request_candidate_timeline_by_endpoint_ids_since(
|
||||
endpoint_ids,
|
||||
@@ -415,10 +382,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_catalog_keys_by_provider_ids(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogKey>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogKey>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_keys_by_provider_ids(provider_ids)
|
||||
.await
|
||||
@@ -428,10 +392,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_catalog_keys_by_ids(
|
||||
&self,
|
||||
key_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogKey>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogKey>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_keys_by_ids(key_ids)
|
||||
.await
|
||||
@@ -440,9 +401,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_provider_catalog_key_page(
|
||||
&self,
|
||||
query: &aether_data::repository::provider_catalog::ProviderCatalogKeyListQuery,
|
||||
) -> Result<aether_data::repository::provider_catalog::StoredProviderCatalogKeyPage, GatewayError>
|
||||
{
|
||||
query: &provider_catalog::ProviderCatalogKeyListQuery,
|
||||
) -> Result<provider_catalog::StoredProviderCatalogKeyPage, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_key_page(query)
|
||||
.await
|
||||
@@ -452,10 +412,7 @@ impl AppState {
|
||||
pub(crate) async fn list_provider_catalog_key_stats_by_provider_ids(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogKeyStats>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogKeyStats>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_key_stats_by_provider_ids(provider_ids)
|
||||
.await
|
||||
@@ -464,11 +421,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn create_provider_catalog_key(
|
||||
&self,
|
||||
key: &aether_data::repository::provider_catalog::StoredProviderCatalogKey,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogKey>,
|
||||
GatewayError,
|
||||
> {
|
||||
key: &provider_catalog::StoredProviderCatalogKey,
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogKey>, GatewayError> {
|
||||
let created = self
|
||||
.data
|
||||
.create_provider_catalog_key(key)
|
||||
@@ -482,12 +436,9 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn create_provider_catalog_provider(
|
||||
&self,
|
||||
provider: &aether_data::repository::provider_catalog::StoredProviderCatalogProvider,
|
||||
provider: &provider_catalog::StoredProviderCatalogProvider,
|
||||
shift_existing_priorities_from: Option<i32>,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogProvider>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogProvider>, GatewayError> {
|
||||
let created = self
|
||||
.data
|
||||
.create_provider_catalog_provider(provider, shift_existing_priorities_from)
|
||||
@@ -501,11 +452,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_provider_catalog_provider(
|
||||
&self,
|
||||
provider: &aether_data::repository::provider_catalog::StoredProviderCatalogProvider,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogProvider>,
|
||||
GatewayError,
|
||||
> {
|
||||
provider: &provider_catalog::StoredProviderCatalogProvider,
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogProvider>, GatewayError> {
|
||||
let updated = self
|
||||
.data
|
||||
.update_provider_catalog_provider(provider)
|
||||
@@ -550,11 +498,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn create_provider_catalog_endpoint(
|
||||
&self,
|
||||
endpoint: &aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint>,
|
||||
GatewayError,
|
||||
> {
|
||||
endpoint: &provider_catalog::StoredProviderCatalogEndpoint,
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogEndpoint>, GatewayError> {
|
||||
let created = self
|
||||
.data
|
||||
.create_provider_catalog_endpoint(endpoint)
|
||||
@@ -568,11 +513,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_provider_catalog_endpoint(
|
||||
&self,
|
||||
endpoint: &aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint>,
|
||||
GatewayError,
|
||||
> {
|
||||
endpoint: &provider_catalog::StoredProviderCatalogEndpoint,
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogEndpoint>, GatewayError> {
|
||||
let updated = self
|
||||
.data
|
||||
.update_provider_catalog_endpoint(endpoint)
|
||||
@@ -601,11 +543,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_provider_catalog_key(
|
||||
&self,
|
||||
key: &aether_data::repository::provider_catalog::StoredProviderCatalogKey,
|
||||
) -> Result<
|
||||
Option<aether_data::repository::provider_catalog::StoredProviderCatalogKey>,
|
||||
GatewayError,
|
||||
> {
|
||||
key: &provider_catalog::StoredProviderCatalogKey,
|
||||
) -> Result<Option<provider_catalog::StoredProviderCatalogKey>, GatewayError> {
|
||||
let updated = self
|
||||
.data
|
||||
.update_provider_catalog_key(key)
|
||||
@@ -664,10 +603,7 @@ impl AppState {
|
||||
pub(crate) async fn read_provider_catalog_providers_by_ids(
|
||||
&self,
|
||||
provider_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogProvider>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogProvider>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_providers_by_ids(provider_ids)
|
||||
.await
|
||||
@@ -677,10 +613,7 @@ impl AppState {
|
||||
pub(crate) async fn read_provider_catalog_endpoints_by_ids(
|
||||
&self,
|
||||
endpoint_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogEndpoint>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogEndpoint>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_endpoints_by_ids(endpoint_ids)
|
||||
.await
|
||||
@@ -690,10 +623,7 @@ impl AppState {
|
||||
pub(crate) async fn read_provider_catalog_keys_by_ids(
|
||||
&self,
|
||||
key_ids: &[String],
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::provider_catalog::StoredProviderCatalogKey>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<provider_catalog::StoredProviderCatalogKey>, GatewayError> {
|
||||
self.data
|
||||
.list_provider_catalog_keys_by_ids(key_ids)
|
||||
.await
|
||||
|
||||
@@ -13,6 +13,7 @@ use aether_runtime::{
|
||||
DistributedConcurrencyError, DistributedConcurrencyGate, DistributedConcurrencySnapshot,
|
||||
MetricKind, MetricLabel, MetricSample,
|
||||
};
|
||||
use aether_scheduler_core::PROVIDER_KEY_RPM_WINDOW_SECS;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use super::{AppState, FrontdoorCorsConfig, LocalExecutionRuntimeMissDiagnostic};
|
||||
@@ -20,18 +21,18 @@ use super::{AppState, FrontdoorCorsConfig, LocalExecutionRuntimeMissDiagnostic};
|
||||
use super::super::async_task::{
|
||||
spawn_video_task_poller, VideoTaskPollerConfig, VideoTaskService, VideoTaskTruthSourceMode,
|
||||
};
|
||||
use super::super::fallback_metrics;
|
||||
use super::super::fallback_metrics::{GatewayFallbackMetricKind, GatewayFallbackReason};
|
||||
use super::super::cache::{
|
||||
AuthApiKeyLastUsedCache, AuthContextCache, DirectPlanBypassCache, SchedulerAffinityCache,
|
||||
SchedulerAffinityTarget,
|
||||
};
|
||||
use super::super::data::{GatewayDataConfig, GatewayDataState};
|
||||
use super::super::fallback_metrics;
|
||||
use super::super::fallback_metrics::{GatewayFallbackMetricKind, GatewayFallbackReason};
|
||||
use super::super::model_fetch::spawn_model_fetch_worker;
|
||||
use super::super::rate_limit::{FrontdoorUserRpmConfig, FrontdoorUserRpmLimiter};
|
||||
use super::super::router::RequestAdmissionError;
|
||||
use super::super::{control::GatewayControlDecision, error::GatewayError};
|
||||
use super::super::{provider_transport, scheduler, usage};
|
||||
use super::super::{provider_transport, usage};
|
||||
|
||||
use crate::maintenance::spawn_audit_cleanup_worker;
|
||||
use crate::maintenance::spawn_db_maintenance_worker;
|
||||
@@ -265,7 +266,7 @@ impl AppState {
|
||||
.provider_key_rpm_resets
|
||||
.lock()
|
||||
.expect("provider key rpm reset cache should lock");
|
||||
let min_kept = now_unix_secs.saturating_sub(scheduler::PROVIDER_KEY_RPM_WINDOW_SECS);
|
||||
let min_kept = now_unix_secs.saturating_sub(PROVIDER_KEY_RPM_WINDOW_SECS);
|
||||
resets.retain(|_, reset_at| *reset_at >= min_kept);
|
||||
resets.insert(key_id.to_string(), now_unix_secs);
|
||||
}
|
||||
@@ -279,7 +280,7 @@ impl AppState {
|
||||
.provider_key_rpm_resets
|
||||
.lock()
|
||||
.expect("provider key rpm reset cache should lock");
|
||||
let min_kept = now_unix_secs.saturating_sub(scheduler::PROVIDER_KEY_RPM_WINDOW_SECS);
|
||||
let min_kept = now_unix_secs.saturating_sub(PROVIDER_KEY_RPM_WINDOW_SECS);
|
||||
resets.retain(|_, reset_at| *reset_at >= min_kept);
|
||||
resets.get(key_id).copied()
|
||||
}
|
||||
@@ -323,8 +324,7 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_system_config_entries(
|
||||
&self,
|
||||
) -> Result<Vec<crate::data::state::StoredSystemConfigEntry>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<crate::data::state::StoredSystemConfigEntry>, GatewayError> {
|
||||
self.data
|
||||
.list_system_config_entries()
|
||||
.await
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use aether_contracts::{ExecutionPlan, ExecutionResult, ProxySnapshot};
|
||||
use aether_data::repository::candidate_selection::StoredMinimalCandidateSelectionRow;
|
||||
use aether_data::repository::candidates::{StoredRequestCandidate, UpsertRequestCandidateRecord};
|
||||
use aether_data::repository::global_models::{
|
||||
use aether_data_contracts::repository::candidates::{
|
||||
StoredRequestCandidate, UpsertRequestCandidateRecord,
|
||||
};
|
||||
use aether_data_contracts::repository::global_models::{
|
||||
AdminGlobalModelListQuery, AdminProviderModelListQuery, StoredAdminGlobalModelPage,
|
||||
StoredAdminProviderModel, UpsertAdminProviderModelRecord,
|
||||
};
|
||||
use aether_data::repository::provider_catalog::{
|
||||
use aether_data_contracts::repository::provider_catalog::{
|
||||
StoredProviderCatalogEndpoint, StoredProviderCatalogKey, StoredProviderCatalogProvider,
|
||||
};
|
||||
use aether_data::repository::quota::StoredProviderQuotaSnapshot;
|
||||
use aether_data::DataLayerError;
|
||||
use aether_data_contracts::repository::quota::StoredProviderQuotaSnapshot;
|
||||
use aether_model_fetch::{
|
||||
aggregate_models_for_cache, model_fetch_interval_minutes, ModelFetchAssociationStore,
|
||||
ModelFetchTransportRuntime,
|
||||
@@ -27,10 +27,10 @@ use crate::provider_transport::{
|
||||
resolve_transport_proxy_snapshot_with_tunnel_affinity, GatewayProviderTransportSnapshot,
|
||||
LocalResolvedOAuthRequestAuth,
|
||||
};
|
||||
use crate::scheduler::{
|
||||
GatewayMinimalCandidateSelectionCandidate, SchedulerCandidateSelectionRowSource,
|
||||
SchedulerRequestCandidateRuntimeState, SchedulerRuntimeState,
|
||||
use crate::request_candidate_runtime::{
|
||||
RequestCandidateRuntimeReader, RequestCandidateRuntimeWriter,
|
||||
};
|
||||
use crate::scheduler::state::SchedulerRuntimeState;
|
||||
use crate::{execution_runtime, provider_transport};
|
||||
|
||||
#[async_trait]
|
||||
@@ -237,17 +237,20 @@ impl ModelFetchAssociationStore for AppState {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SchedulerRequestCandidateRuntimeState for AppState {
|
||||
fn has_request_candidate_data_writer(&self) -> bool {
|
||||
AppState::has_request_candidate_data_writer(self)
|
||||
}
|
||||
|
||||
impl RequestCandidateRuntimeReader for AppState {
|
||||
async fn read_request_candidates_by_request_id(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<Vec<StoredRequestCandidate>, GatewayError> {
|
||||
AppState::read_request_candidates_by_request_id(self, request_id).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RequestCandidateRuntimeWriter for AppState {
|
||||
fn has_request_candidate_data_writer(&self) -> bool {
|
||||
AppState::has_request_candidate_data_writer(self)
|
||||
}
|
||||
|
||||
async fn upsert_request_candidate(
|
||||
&self,
|
||||
@@ -257,28 +260,6 @@ impl SchedulerRequestCandidateRuntimeState for AppState {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SchedulerCandidateSelectionRowSource for AppState {
|
||||
async fn read_minimal_candidate_selection_rows_for_api_format_and_global_model(
|
||||
&self,
|
||||
api_format: &str,
|
||||
global_model_name: &str,
|
||||
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, DataLayerError> {
|
||||
self.data
|
||||
.list_minimal_candidate_selection_rows(api_format, global_model_name)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_minimal_candidate_selection_rows_for_api_format(
|
||||
&self,
|
||||
api_format: &str,
|
||||
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, DataLayerError> {
|
||||
self.data
|
||||
.list_minimal_candidate_selection_rows_for_api_format(api_format)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SchedulerRuntimeState for AppState {
|
||||
async fn read_provider_quota_snapshot(
|
||||
@@ -309,23 +290,6 @@ impl SchedulerRuntimeState for AppState {
|
||||
AppState::read_recent_request_candidates(self, limit).await
|
||||
}
|
||||
|
||||
async fn read_minimal_candidate_selection(
|
||||
&self,
|
||||
api_format: &str,
|
||||
global_model_name: &str,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: Option<&crate::data::auth::GatewayAuthApiKeySnapshot>,
|
||||
) -> Result<Vec<GatewayMinimalCandidateSelectionCandidate>, GatewayError> {
|
||||
AppState::read_minimal_candidate_selection(
|
||||
self,
|
||||
api_format,
|
||||
global_model_name,
|
||||
require_streaming,
|
||||
auth_snapshot,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn provider_key_rpm_reset_at(&self, key_id: &str, now_unix_secs: u64) -> Option<u64> {
|
||||
AppState::provider_key_rpm_reset_at(self, key_id, now_unix_secs)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::error::GatewayError;
|
||||
use super::data::GatewayDataState;
|
||||
use super::error::GatewayError;
|
||||
|
||||
mod admin_types;
|
||||
mod app;
|
||||
@@ -29,7 +29,8 @@ pub(crate) use self::cache::{
|
||||
};
|
||||
pub use self::cors::FrontdoorCorsConfig;
|
||||
pub(crate) use self::types::{
|
||||
AdminWalletMutationOutcome, LocalExecutionRuntimeMissDiagnostic, LocalMutationOutcome,
|
||||
AdminWalletMutationOutcome, GatewayAdminPaymentCallbackView, GatewayUserPreferenceView,
|
||||
GatewayUserSessionView, LocalExecutionRuntimeMissDiagnostic, LocalMutationOutcome,
|
||||
LocalProviderDeleteTaskState,
|
||||
};
|
||||
use super::provider_transport::provider_transport_snapshot_looks_refreshed;
|
||||
|
||||
@@ -64,10 +64,8 @@ impl AppState {
|
||||
provider_id: &str,
|
||||
endpoint_id: &str,
|
||||
key_id: &str,
|
||||
) -> Result<
|
||||
Option<crate::provider_transport::GatewayProviderTransportSnapshot>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Option<crate::provider_transport::GatewayProviderTransportSnapshot>, GatewayError>
|
||||
{
|
||||
self.data
|
||||
.read_provider_transport_snapshot(provider_id, endpoint_id, key_id)
|
||||
.await
|
||||
@@ -318,10 +316,8 @@ impl AppState {
|
||||
provider_id: &str,
|
||||
endpoint_id: &str,
|
||||
key_id: &str,
|
||||
) -> Result<
|
||||
Option<crate::provider_transport::GatewayProviderTransportSnapshot>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Option<crate::provider_transport::GatewayProviderTransportSnapshot>, GatewayError>
|
||||
{
|
||||
let Some(cache_key) =
|
||||
ProviderTransportSnapshotCacheKey::new(provider_id, endpoint_id, key_id)
|
||||
else {
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
use crate::{scheduler, AppState, GatewayError};
|
||||
use aether_data::repository::audit::RequestAuditBundle;
|
||||
use aether_data::repository::usage::StoredRequestUsageAudit;
|
||||
|
||||
use super::super::{AUTH_API_KEY_LAST_USED_MAX_ENTRIES, AUTH_API_KEY_LAST_USED_TTL};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn read_request_candidate_trace(
|
||||
&self,
|
||||
request_id: &str,
|
||||
attempted_only: bool,
|
||||
) -> Result<Option<crate::data::candidates::RequestCandidateTrace>, GatewayError>
|
||||
{
|
||||
self.data
|
||||
.read_request_candidate_trace(request_id, attempted_only)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_decision_trace(
|
||||
&self,
|
||||
request_id: &str,
|
||||
attempted_only: bool,
|
||||
) -> Result<Option<crate::data::decision_trace::DecisionTrace>, GatewayError>
|
||||
{
|
||||
self.data
|
||||
.read_decision_trace(request_id, attempted_only)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_request_usage_audit(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<Option<StoredRequestUsageAudit>, GatewayError> {
|
||||
self.data
|
||||
.read_request_usage_audit(request_id)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn find_request_usage_by_id(
|
||||
&self,
|
||||
usage_id: &str,
|
||||
) -> Result<Option<aether_data::repository::usage::StoredRequestUsageAudit>, GatewayError> {
|
||||
self.data
|
||||
.find_request_usage_by_id(usage_id)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_request_audit_bundle(
|
||||
&self,
|
||||
request_id: &str,
|
||||
attempted_only: bool,
|
||||
now_unix_secs: u64,
|
||||
) -> Result<Option<RequestAuditBundle>, GatewayError> {
|
||||
self.data
|
||||
.read_request_audit_bundle(request_id, attempted_only, now_unix_secs)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_auth_api_key_snapshot(
|
||||
&self,
|
||||
user_id: &str,
|
||||
api_key_id: &str,
|
||||
now_unix_secs: u64,
|
||||
) -> Result<Option<crate::data::auth::GatewayAuthApiKeySnapshot>, GatewayError>
|
||||
{
|
||||
self.data
|
||||
.read_auth_api_key_snapshot(user_id, api_key_id, now_unix_secs)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_auth_api_key_snapshot_by_key_hash(
|
||||
&self,
|
||||
key_hash: &str,
|
||||
now_unix_secs: u64,
|
||||
) -> Result<Option<crate::data::auth::GatewayAuthApiKeySnapshot>, GatewayError>
|
||||
{
|
||||
self.data
|
||||
.read_auth_api_key_snapshot_by_key_hash(key_hash, now_unix_secs)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_auth_api_key_snapshots_by_ids(
|
||||
&self,
|
||||
api_key_ids: &[String],
|
||||
) -> Result<Vec<aether_data::repository::auth::StoredAuthApiKeySnapshot>, GatewayError> {
|
||||
self.data
|
||||
.list_auth_api_key_snapshots_by_ids(api_key_ids)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn has_auth_api_key_writer(&self) -> bool {
|
||||
self.data.has_auth_api_key_writer()
|
||||
}
|
||||
|
||||
pub(crate) async fn touch_auth_api_key_last_used_best_effort(&self, api_key_id: &str) {
|
||||
let api_key_id = api_key_id.trim();
|
||||
if api_key_id.is_empty() || !self.has_auth_api_key_writer() {
|
||||
return;
|
||||
}
|
||||
if !self.auth_api_key_last_used_cache.should_touch(
|
||||
api_key_id,
|
||||
AUTH_API_KEY_LAST_USED_TTL,
|
||||
AUTH_API_KEY_LAST_USED_MAX_ENTRIES,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.data.touch_auth_api_key_last_used(api_key_id).await {
|
||||
tracing::warn!(
|
||||
api_key_id = %api_key_id,
|
||||
error = ?err,
|
||||
"gateway auth api key last_used_at touch failed"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn read_minimal_candidate_selection(
|
||||
&self,
|
||||
api_format: &str,
|
||||
global_model_name: &str,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: Option<&crate::data::auth::GatewayAuthApiKeySnapshot>,
|
||||
) -> Result<Vec<scheduler::GatewayMinimalCandidateSelectionCandidate>, GatewayError> {
|
||||
scheduler::read_minimal_candidate_selection(
|
||||
self.data.as_ref(),
|
||||
api_format,
|
||||
global_model_name,
|
||||
require_streaming,
|
||||
auth_snapshot,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
}
|
||||
26
apps/aether-gateway/src/state/runtime/auth/api_keys.rs
Normal file
26
apps/aether-gateway/src/state/runtime/auth/api_keys.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use crate::AppState;
|
||||
|
||||
use super::super::super::{AUTH_API_KEY_LAST_USED_MAX_ENTRIES, AUTH_API_KEY_LAST_USED_TTL};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn touch_auth_api_key_last_used_best_effort(&self, api_key_id: &str) {
|
||||
let api_key_id = api_key_id.trim();
|
||||
if api_key_id.is_empty() || !self.data.has_auth_api_key_writer() {
|
||||
return;
|
||||
}
|
||||
if !self.auth_api_key_last_used_cache.should_touch(
|
||||
api_key_id,
|
||||
AUTH_API_KEY_LAST_USED_TTL,
|
||||
AUTH_API_KEY_LAST_USED_MAX_ENTRIES,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.data.touch_auth_api_key_last_used(api_key_id).await {
|
||||
tracing::warn!(
|
||||
api_key_id = %api_key_id,
|
||||
error = ?err,
|
||||
"gateway auth api key last_used_at touch failed"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod api_keys;
|
||||
mod sessions;
|
||||
mod user_lifecycle;
|
||||
mod user_provisioning;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::{AppState, GatewayError};
|
||||
use crate::{AppState, GatewayError, GatewayUserSessionView};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn find_user_session(
|
||||
&self,
|
||||
user_id: &str,
|
||||
session_id: &str,
|
||||
) -> Result<Option<crate::data::state::StoredUserSessionRecord>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<GatewayUserSessionView>, GatewayError> {
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.auth_session_store.as_ref() {
|
||||
let key = format!("{user_id}:{session_id}");
|
||||
@@ -14,20 +13,21 @@ impl AppState {
|
||||
.lock()
|
||||
.expect("auth session store should lock")
|
||||
.get(&key)
|
||||
.cloned());
|
||||
.cloned()
|
||||
.map(Into::into));
|
||||
}
|
||||
|
||||
self.data
|
||||
.find_user_session(user_id, session_id)
|
||||
.await
|
||||
.map(|value| value.map(Into::into))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn list_user_sessions(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<crate::data::state::StoredUserSessionRecord>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<GatewayUserSessionView>, GatewayError> {
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.auth_session_store.as_ref() {
|
||||
let prefix = format!("{user_id}:");
|
||||
@@ -46,12 +46,13 @@ impl AppState {
|
||||
.cmp(&left.last_seen_at)
|
||||
.then_with(|| right.created_at.cmp(&left.created_at))
|
||||
});
|
||||
return Ok(sessions);
|
||||
return Ok(sessions.into_iter().map(Into::into).collect());
|
||||
}
|
||||
|
||||
self.data
|
||||
.list_user_sessions(user_id)
|
||||
.await
|
||||
.map(|sessions| sessions.into_iter().map(Into::into).collect())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
@@ -112,11 +113,14 @@ impl AppState {
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn create_user_session(
|
||||
pub(crate) async fn create_user_session<T>(
|
||||
&self,
|
||||
session: crate::data::state::StoredUserSessionRecord,
|
||||
) -> Result<Option<crate::data::state::StoredUserSessionRecord>, GatewayError>
|
||||
session: T,
|
||||
) -> Result<Option<GatewayUserSessionView>, GatewayError>
|
||||
where
|
||||
T: Into<GatewayUserSessionView>,
|
||||
{
|
||||
let session = session.into();
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.auth_session_store.as_ref() {
|
||||
let now = session
|
||||
@@ -138,14 +142,16 @@ impl AppState {
|
||||
}
|
||||
guard.insert(
|
||||
format!("{}:{}", session.user_id, session.id),
|
||||
session.clone(),
|
||||
session.clone().into(),
|
||||
);
|
||||
return Ok(Some(session));
|
||||
}
|
||||
|
||||
let raw_session: crate::data::state::StoredUserSessionRecord = session.into();
|
||||
self.data
|
||||
.create_user_session(&session)
|
||||
.create_user_session(&raw_session)
|
||||
.await
|
||||
.map(|value| value.map(Into::into))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@ use aether_data::repository::wallet::{
|
||||
StoredAdminWalletRefundRequestItem, StoredAdminWalletTransaction,
|
||||
};
|
||||
|
||||
use crate::state::AdminPaymentCallbackRecord;
|
||||
use crate::{
|
||||
AdminWalletMutationOutcome, AdminWalletPaymentOrderRecord, AdminWalletRefundRecord, AppState,
|
||||
GatewayError,
|
||||
GatewayAdminPaymentCallbackView, GatewayError,
|
||||
};
|
||||
|
||||
impl AppState {
|
||||
@@ -124,7 +123,7 @@ impl AppState {
|
||||
payment_method: Option<&str>,
|
||||
limit: usize,
|
||||
offset: usize,
|
||||
) -> Result<Option<(Vec<AdminPaymentCallbackRecord>, u64)>, GatewayError> {
|
||||
) -> Result<Option<(Vec<GatewayAdminPaymentCallbackView>, u64)>, GatewayError> {
|
||||
#[cfg(test)]
|
||||
if let Some(store) = self.admin_payment_callback_store.as_ref() {
|
||||
let mut items = store
|
||||
@@ -147,6 +146,7 @@ impl AppState {
|
||||
.into_iter()
|
||||
.skip(offset)
|
||||
.take(limit)
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>();
|
||||
return Ok(Some((items, total)));
|
||||
}
|
||||
@@ -160,6 +160,7 @@ impl AppState {
|
||||
page.items
|
||||
.into_iter()
|
||||
.map(stored_admin_payment_callback_to_gateway)
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
page.total,
|
||||
)))
|
||||
@@ -415,8 +416,8 @@ fn stored_admin_payment_order_to_gateway(
|
||||
|
||||
fn stored_admin_payment_callback_to_gateway(
|
||||
record: StoredAdminPaymentCallback,
|
||||
) -> AdminPaymentCallbackRecord {
|
||||
AdminPaymentCallbackRecord {
|
||||
) -> GatewayAdminPaymentCallbackView {
|
||||
GatewayAdminPaymentCallbackView {
|
||||
id: record.id,
|
||||
payment_order_id: record.payment_order_id,
|
||||
payment_method: record.payment_method,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::{AppState, GatewayError};
|
||||
use aether_data_contracts::repository::{candidate_selection, candidates, quota};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn list_minimal_candidate_selection_rows_for_api_format(
|
||||
&self,
|
||||
api_format: &str,
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::candidate_selection::StoredMinimalCandidateSelectionRow>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<candidate_selection::StoredMinimalCandidateSelectionRow>, GatewayError> {
|
||||
self.data
|
||||
.list_minimal_candidate_selection_rows_for_api_format(api_format)
|
||||
.await
|
||||
@@ -18,10 +16,7 @@ impl AppState {
|
||||
&self,
|
||||
api_format: &str,
|
||||
global_model_name: &str,
|
||||
) -> Result<
|
||||
Vec<aether_data::repository::candidate_selection::StoredMinimalCandidateSelectionRow>,
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<Vec<candidate_selection::StoredMinimalCandidateSelectionRow>, GatewayError> {
|
||||
self.data
|
||||
.list_minimal_candidate_selection_rows(api_format, global_model_name)
|
||||
.await
|
||||
@@ -31,8 +26,7 @@ impl AppState {
|
||||
pub(crate) async fn read_provider_quota_snapshot(
|
||||
&self,
|
||||
provider_id: &str,
|
||||
) -> Result<Option<aether_data::repository::quota::StoredProviderQuotaSnapshot>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<quota::StoredProviderQuotaSnapshot>, GatewayError> {
|
||||
self.data
|
||||
.find_provider_quota_by_provider_id(provider_id)
|
||||
.await
|
||||
@@ -42,8 +36,7 @@ impl AppState {
|
||||
pub(crate) async fn read_recent_request_candidates(
|
||||
&self,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::candidates::StoredRequestCandidate>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::StoredRequestCandidate>, GatewayError> {
|
||||
self.data
|
||||
.list_recent_request_candidates(limit)
|
||||
.await
|
||||
@@ -52,9 +45,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn upsert_request_candidate(
|
||||
&self,
|
||||
candidate: aether_data::repository::candidates::UpsertRequestCandidateRecord,
|
||||
) -> Result<Option<aether_data::repository::candidates::StoredRequestCandidate>, GatewayError>
|
||||
{
|
||||
candidate: candidates::UpsertRequestCandidateRecord,
|
||||
) -> Result<Option<candidates::StoredRequestCandidate>, GatewayError> {
|
||||
self.data
|
||||
.upsert_request_candidate(candidate)
|
||||
.await
|
||||
|
||||
@@ -5,12 +5,10 @@ use super::{
|
||||
AdminBillingRuleRecord, AdminBillingRuleWriteInput, AdminPaymentCallbackRecord,
|
||||
AdminSecurityBlacklistEntry, AdminWalletMutationOutcome, AdminWalletPaymentOrderRecord,
|
||||
AdminWalletRefundRecord, AdminWalletTransactionRecord, AppState, LocalMutationOutcome,
|
||||
AUTH_API_KEY_LAST_USED_MAX_ENTRIES, AUTH_API_KEY_LAST_USED_TTL,
|
||||
};
|
||||
|
||||
mod announcements;
|
||||
mod api_key_exports;
|
||||
mod audit;
|
||||
mod auth;
|
||||
mod billing;
|
||||
mod candidate_queries;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use super::admin_payment_gateway_response_map;
|
||||
use crate::{
|
||||
AdminWalletMutationOutcome, AdminWalletPaymentOrderRecord, AppState, GatewayError,
|
||||
};
|
||||
use crate::{AdminWalletMutationOutcome, AdminWalletPaymentOrderRecord, AppState, GatewayError};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn admin_expire_payment_order(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{AppState, GatewayError};
|
||||
use aether_data_contracts::repository::{candidates, usage};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn read_request_candidates_by_request_id(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<Vec<aether_data::repository::candidates::StoredRequestCandidate>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::StoredRequestCandidate>, GatewayError> {
|
||||
self.data
|
||||
.list_request_candidates_by_request_id(request_id)
|
||||
.await
|
||||
@@ -16,8 +16,7 @@ impl AppState {
|
||||
&self,
|
||||
provider_id: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::candidates::StoredRequestCandidate>, GatewayError>
|
||||
{
|
||||
) -> Result<Vec<candidates::StoredRequestCandidate>, GatewayError> {
|
||||
self.data
|
||||
.list_request_candidates_by_provider_id(provider_id, limit)
|
||||
.await
|
||||
@@ -28,7 +27,7 @@ impl AppState {
|
||||
&self,
|
||||
provider_id: &str,
|
||||
since_unix_secs: u64,
|
||||
) -> Result<aether_data::repository::usage::StoredProviderUsageSummary, GatewayError> {
|
||||
) -> Result<usage::StoredProviderUsageSummary, GatewayError> {
|
||||
self.data
|
||||
.summarize_provider_usage_since(provider_id, since_unix_secs)
|
||||
.await
|
||||
@@ -37,8 +36,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_usage_audits(
|
||||
&self,
|
||||
query: &aether_data::repository::usage::UsageAuditListQuery,
|
||||
) -> Result<Vec<aether_data::repository::usage::StoredRequestUsageAudit>, GatewayError> {
|
||||
query: &usage::UsageAuditListQuery,
|
||||
) -> Result<Vec<usage::StoredRequestUsageAudit>, GatewayError> {
|
||||
self.data
|
||||
.list_usage_audits(query)
|
||||
.await
|
||||
@@ -49,7 +48,7 @@ impl AppState {
|
||||
&self,
|
||||
user_id: Option<&str>,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::usage::StoredRequestUsageAudit>, GatewayError> {
|
||||
) -> Result<Vec<usage::StoredRequestUsageAudit>, GatewayError> {
|
||||
self.data
|
||||
.list_recent_usage_audits(user_id, limit)
|
||||
.await
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
use crate::{AppState, GatewayError};
|
||||
use crate::{AppState, GatewayError, GatewayUserPreferenceView};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn read_user_preferences(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> Result<Option<crate::data::state::StoredUserPreferenceRecord>, GatewayError>
|
||||
{
|
||||
) -> Result<Option<GatewayUserPreferenceView>, GatewayError> {
|
||||
self.data
|
||||
.read_user_preferences(user_id)
|
||||
.await
|
||||
.map(|value| value.map(Into::into))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn write_user_preferences(
|
||||
pub(crate) async fn write_user_preferences<T>(
|
||||
&self,
|
||||
preferences: &crate::data::state::StoredUserPreferenceRecord,
|
||||
) -> Result<Option<crate::data::state::StoredUserPreferenceRecord>, GatewayError>
|
||||
preferences: T,
|
||||
) -> Result<Option<GatewayUserPreferenceView>, GatewayError>
|
||||
where
|
||||
T: Into<GatewayUserPreferenceView>,
|
||||
{
|
||||
let preferences = preferences.into();
|
||||
let raw_preferences: crate::data::state::StoredUserPreferenceRecord = preferences.into();
|
||||
self.data
|
||||
.write_user_preferences(preferences)
|
||||
.write_user_preferences(&raw_preferences)
|
||||
.await
|
||||
.map(|value| value.map(Into::into))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::{
|
||||
AdminWalletPaymentOrderRecord, AdminWalletTransactionRecord, AppState, GatewayError,
|
||||
};
|
||||
use crate::{AdminWalletPaymentOrderRecord, AdminWalletTransactionRecord, AppState, GatewayError};
|
||||
|
||||
use super::admin_wallet_build_order_no;
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex as StdMutex};
|
||||
|
||||
use aether_data_contracts::repository::candidates::RequestCandidateReadRepository;
|
||||
use aether_data_contracts::repository::provider_catalog::ProviderCatalogReadRepository;
|
||||
use aether_data_contracts::repository::usage::{UsageReadRepository, UsageRepository};
|
||||
use aether_data_contracts::repository::video_tasks::{
|
||||
VideoTaskReadRepository, VideoTaskRepository,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use super::{AppState, GatewayDataState};
|
||||
@@ -20,18 +26,14 @@ impl AppState {
|
||||
) -> Self {
|
||||
self.tunnel = crate::tunnel::EmbeddedTunnelState::with_data_and_directory(
|
||||
Arc::clone(&self.data),
|
||||
crate::tunnel::TunnelAttachmentDirectory::for_tests(
|
||||
instance_id,
|
||||
relay_base_url,
|
||||
90,
|
||||
),
|
||||
crate::tunnel::TunnelAttachmentDirectory::for_tests(instance_id, relay_base_url, 90),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_video_task_data_reader_for_tests(
|
||||
mut self,
|
||||
repository: Arc<dyn aether_data::repository::video_tasks::VideoTaskReadRepository>,
|
||||
repository: Arc<dyn VideoTaskReadRepository>,
|
||||
) -> Self {
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_video_task_reader_for_tests(repository),
|
||||
@@ -41,7 +43,7 @@ impl AppState {
|
||||
|
||||
pub(crate) fn with_video_task_data_repository_for_tests<T>(mut self, repository: Arc<T>) -> Self
|
||||
where
|
||||
T: aether_data::repository::video_tasks::VideoTaskRepository + 'static,
|
||||
T: VideoTaskRepository + 'static,
|
||||
{
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_video_task_repository_for_tests(repository),
|
||||
@@ -52,13 +54,11 @@ impl AppState {
|
||||
pub(crate) fn with_video_task_repository_and_provider_transport_for_tests<T>(
|
||||
mut self,
|
||||
repository: Arc<T>,
|
||||
provider_catalog_repository: Arc<
|
||||
dyn aether_data::repository::provider_catalog::ProviderCatalogReadRepository,
|
||||
>,
|
||||
provider_catalog_repository: Arc<dyn ProviderCatalogReadRepository>,
|
||||
encryption_key: impl Into<String>,
|
||||
) -> Self
|
||||
where
|
||||
T: aether_data::repository::video_tasks::VideoTaskRepository + 'static,
|
||||
T: VideoTaskRepository + 'static,
|
||||
{
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_video_task_repository_and_provider_transport_for_tests(
|
||||
@@ -72,7 +72,7 @@ impl AppState {
|
||||
|
||||
pub(crate) fn with_request_candidate_data_reader_for_tests(
|
||||
mut self,
|
||||
repository: Arc<dyn aether_data::repository::candidates::RequestCandidateReadRepository>,
|
||||
repository: Arc<dyn RequestCandidateReadRepository>,
|
||||
) -> Self {
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_request_candidate_reader_for_tests(repository),
|
||||
@@ -82,12 +82,8 @@ impl AppState {
|
||||
|
||||
pub(crate) fn with_decision_trace_data_readers_for_tests(
|
||||
mut self,
|
||||
request_candidate_repository: Arc<
|
||||
dyn aether_data::repository::candidates::RequestCandidateReadRepository,
|
||||
>,
|
||||
provider_catalog_repository: Arc<
|
||||
dyn aether_data::repository::provider_catalog::ProviderCatalogReadRepository,
|
||||
>,
|
||||
request_candidate_repository: Arc<dyn RequestCandidateReadRepository>,
|
||||
provider_catalog_repository: Arc<dyn ProviderCatalogReadRepository>,
|
||||
) -> Self {
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_decision_trace_readers_for_tests(
|
||||
@@ -101,13 +97,9 @@ impl AppState {
|
||||
pub(crate) fn with_request_audit_data_readers_for_tests(
|
||||
mut self,
|
||||
auth_api_key_repository: Arc<dyn aether_data::repository::auth::AuthApiKeyReadRepository>,
|
||||
request_candidate_repository: Arc<
|
||||
dyn aether_data::repository::candidates::RequestCandidateReadRepository,
|
||||
>,
|
||||
provider_catalog_repository: Arc<
|
||||
dyn aether_data::repository::provider_catalog::ProviderCatalogReadRepository,
|
||||
>,
|
||||
usage_repository: Arc<dyn aether_data::repository::usage::UsageReadRepository>,
|
||||
request_candidate_repository: Arc<dyn RequestCandidateReadRepository>,
|
||||
provider_catalog_repository: Arc<dyn ProviderCatalogReadRepository>,
|
||||
usage_repository: Arc<dyn UsageReadRepository>,
|
||||
) -> Self {
|
||||
self.replace_data_state(Arc::new(
|
||||
GatewayDataState::with_request_audit_readers_for_tests(
|
||||
@@ -141,7 +133,7 @@ impl AppState {
|
||||
|
||||
pub(crate) fn with_usage_data_reader_for_tests(
|
||||
mut self,
|
||||
repository: Arc<dyn aether_data::repository::usage::UsageReadRepository>,
|
||||
repository: Arc<dyn UsageReadRepository>,
|
||||
) -> Self {
|
||||
self.replace_data_state(Arc::new(GatewayDataState::with_usage_reader_for_tests(
|
||||
repository,
|
||||
@@ -161,7 +153,7 @@ impl AppState {
|
||||
|
||||
pub(crate) fn with_usage_data_repository_for_tests<T>(mut self, repository: Arc<T>) -> Self
|
||||
where
|
||||
T: aether_data::repository::usage::UsageRepository + 'static,
|
||||
T: UsageRepository + 'static,
|
||||
{
|
||||
self.replace_data_state(Arc::new(GatewayDataState::with_usage_repository_for_tests(
|
||||
repository,
|
||||
|
||||
@@ -54,3 +54,287 @@ pub(crate) enum AdminWalletMutationOutcome<T> {
|
||||
Invalid(String),
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct GatewayUserSessionView {
|
||||
pub(crate) id: String,
|
||||
pub(crate) user_id: String,
|
||||
pub(crate) client_device_id: String,
|
||||
pub(crate) device_label: Option<String>,
|
||||
pub(crate) refresh_token_hash: String,
|
||||
pub(crate) prev_refresh_token_hash: Option<String>,
|
||||
pub(crate) rotated_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub(crate) last_seen_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub(crate) expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub(crate) revoked_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub(crate) revoke_reason: Option<String>,
|
||||
pub(crate) ip_address: Option<String>,
|
||||
pub(crate) user_agent: Option<String>,
|
||||
pub(crate) created_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub(crate) updated_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
impl GatewayUserSessionView {
|
||||
pub(crate) const REFRESH_GRACE_SECONDS: i64 = 10;
|
||||
pub(crate) const TOUCH_INTERVAL_SECONDS: i64 = 300;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
id: String,
|
||||
user_id: String,
|
||||
client_device_id: String,
|
||||
device_label: Option<String>,
|
||||
refresh_token_hash: String,
|
||||
prev_refresh_token_hash: Option<String>,
|
||||
rotated_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
last_seen_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
revoked_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
revoke_reason: Option<String>,
|
||||
ip_address: Option<String>,
|
||||
user_agent: Option<String>,
|
||||
created_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
updated_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
) -> Result<Self, String> {
|
||||
if id.trim().is_empty() {
|
||||
return Err("user_sessions.id is empty".to_string());
|
||||
}
|
||||
if user_id.trim().is_empty() {
|
||||
return Err("user_sessions.user_id is empty".to_string());
|
||||
}
|
||||
if client_device_id.trim().is_empty() {
|
||||
return Err("user_sessions.client_device_id is empty".to_string());
|
||||
}
|
||||
if refresh_token_hash.trim().is_empty() {
|
||||
return Err("user_sessions.refresh_token_hash is empty".to_string());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
user_id,
|
||||
client_device_id,
|
||||
device_label,
|
||||
refresh_token_hash,
|
||||
prev_refresh_token_hash,
|
||||
rotated_at,
|
||||
last_seen_at,
|
||||
expires_at,
|
||||
revoked_at,
|
||||
revoke_reason,
|
||||
ip_address,
|
||||
user_agent,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn hash_refresh_token(token: &str) -> String {
|
||||
use sha2::Digest;
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(token.as_bytes());
|
||||
format!("{:x}", hasher.finalize())
|
||||
}
|
||||
|
||||
pub(crate) fn verify_refresh_token(
|
||||
&self,
|
||||
token: &str,
|
||||
now: chrono::DateTime<chrono::Utc>,
|
||||
) -> (bool, bool) {
|
||||
let token_hash = Self::hash_refresh_token(token);
|
||||
if self.refresh_token_hash == token_hash {
|
||||
return (true, false);
|
||||
}
|
||||
let Some(prev_hash) = self.prev_refresh_token_hash.as_ref() else {
|
||||
return (false, false);
|
||||
};
|
||||
let Some(rotated_at) = self.rotated_at else {
|
||||
return (false, false);
|
||||
};
|
||||
if prev_hash == &token_hash
|
||||
&& now.signed_duration_since(rotated_at).num_seconds() <= Self::REFRESH_GRACE_SECONDS
|
||||
{
|
||||
return (true, true);
|
||||
}
|
||||
(false, false)
|
||||
}
|
||||
|
||||
pub(crate) fn is_revoked(&self) -> bool {
|
||||
self.revoked_at.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn is_expired(&self, now: chrono::DateTime<chrono::Utc>) -> bool {
|
||||
self.expires_at.is_none_or(|expires_at| expires_at <= now)
|
||||
}
|
||||
|
||||
pub(crate) fn should_touch(&self, now: chrono::DateTime<chrono::Utc>) -> bool {
|
||||
self.last_seen_at
|
||||
.map(|last_seen_at| {
|
||||
now.signed_duration_since(last_seen_at).num_seconds()
|
||||
>= Self::TOUCH_INTERVAL_SECONDS
|
||||
})
|
||||
.unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::data::state::StoredUserSessionRecord> for GatewayUserSessionView {
|
||||
fn from(value: crate::data::state::StoredUserSessionRecord) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
user_id: value.user_id,
|
||||
client_device_id: value.client_device_id,
|
||||
device_label: value.device_label,
|
||||
refresh_token_hash: value.refresh_token_hash,
|
||||
prev_refresh_token_hash: value.prev_refresh_token_hash,
|
||||
rotated_at: value.rotated_at,
|
||||
last_seen_at: value.last_seen_at,
|
||||
expires_at: value.expires_at,
|
||||
revoked_at: value.revoked_at,
|
||||
revoke_reason: value.revoke_reason,
|
||||
ip_address: value.ip_address,
|
||||
user_agent: value.user_agent,
|
||||
created_at: value.created_at,
|
||||
updated_at: value.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GatewayUserSessionView> for GatewayUserSessionView {
|
||||
fn from(value: &GatewayUserSessionView) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GatewayUserSessionView> for crate::data::state::StoredUserSessionRecord {
|
||||
fn from(value: GatewayUserSessionView) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
user_id: value.user_id,
|
||||
client_device_id: value.client_device_id,
|
||||
device_label: value.device_label,
|
||||
refresh_token_hash: value.refresh_token_hash,
|
||||
prev_refresh_token_hash: value.prev_refresh_token_hash,
|
||||
rotated_at: value.rotated_at,
|
||||
last_seen_at: value.last_seen_at,
|
||||
expires_at: value.expires_at,
|
||||
revoked_at: value.revoked_at,
|
||||
revoke_reason: value.revoke_reason,
|
||||
ip_address: value.ip_address,
|
||||
user_agent: value.user_agent,
|
||||
created_at: value.created_at,
|
||||
updated_at: value.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) struct GatewayUserPreferenceView {
|
||||
pub(crate) user_id: String,
|
||||
pub(crate) avatar_url: Option<String>,
|
||||
pub(crate) bio: Option<String>,
|
||||
pub(crate) default_provider_id: Option<String>,
|
||||
pub(crate) default_provider_name: Option<String>,
|
||||
pub(crate) theme: String,
|
||||
pub(crate) language: String,
|
||||
pub(crate) timezone: String,
|
||||
pub(crate) email_notifications: bool,
|
||||
pub(crate) usage_alerts: bool,
|
||||
pub(crate) announcement_notifications: bool,
|
||||
}
|
||||
|
||||
impl GatewayUserPreferenceView {
|
||||
pub(crate) fn default_for_user(user_id: impl Into<String>) -> Self {
|
||||
Self {
|
||||
user_id: user_id.into(),
|
||||
avatar_url: None,
|
||||
bio: None,
|
||||
default_provider_id: None,
|
||||
default_provider_name: None,
|
||||
theme: "light".to_string(),
|
||||
language: "zh-CN".to_string(),
|
||||
timezone: "Asia/Shanghai".to_string(),
|
||||
email_notifications: true,
|
||||
usage_alerts: true,
|
||||
announcement_notifications: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::data::state::StoredUserPreferenceRecord> for GatewayUserPreferenceView {
|
||||
fn from(value: crate::data::state::StoredUserPreferenceRecord) -> Self {
|
||||
Self {
|
||||
user_id: value.user_id,
|
||||
avatar_url: value.avatar_url,
|
||||
bio: value.bio,
|
||||
default_provider_id: value.default_provider_id,
|
||||
default_provider_name: value.default_provider_name,
|
||||
theme: value.theme,
|
||||
language: value.language,
|
||||
timezone: value.timezone,
|
||||
email_notifications: value.email_notifications,
|
||||
usage_alerts: value.usage_alerts,
|
||||
announcement_notifications: value.announcement_notifications,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GatewayUserPreferenceView> for GatewayUserPreferenceView {
|
||||
fn from(value: &GatewayUserPreferenceView) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GatewayUserPreferenceView> for crate::data::state::StoredUserPreferenceRecord {
|
||||
fn from(value: GatewayUserPreferenceView) -> Self {
|
||||
Self {
|
||||
user_id: value.user_id,
|
||||
avatar_url: value.avatar_url,
|
||||
bio: value.bio,
|
||||
default_provider_id: value.default_provider_id,
|
||||
default_provider_name: value.default_provider_name,
|
||||
theme: value.theme,
|
||||
language: value.language,
|
||||
timezone: value.timezone,
|
||||
email_notifications: value.email_notifications,
|
||||
usage_alerts: value.usage_alerts,
|
||||
announcement_notifications: value.announcement_notifications,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) struct GatewayAdminPaymentCallbackView {
|
||||
pub(crate) id: String,
|
||||
pub(crate) payment_order_id: Option<String>,
|
||||
pub(crate) payment_method: String,
|
||||
pub(crate) callback_key: String,
|
||||
pub(crate) order_no: Option<String>,
|
||||
pub(crate) gateway_order_id: Option<String>,
|
||||
pub(crate) payload_hash: Option<String>,
|
||||
pub(crate) signature_valid: bool,
|
||||
pub(crate) status: String,
|
||||
pub(crate) payload: Option<serde_json::Value>,
|
||||
pub(crate) error_message: Option<String>,
|
||||
pub(crate) created_at_unix_secs: u64,
|
||||
pub(crate) processed_at_unix_secs: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<super::AdminPaymentCallbackRecord> for GatewayAdminPaymentCallbackView {
|
||||
fn from(value: super::AdminPaymentCallbackRecord) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
payment_order_id: value.payment_order_id,
|
||||
payment_method: value.payment_method,
|
||||
callback_key: value.callback_key,
|
||||
order_no: value.order_no,
|
||||
gateway_order_id: value.gateway_order_id,
|
||||
payload_hash: value.payload_hash,
|
||||
signature_valid: value.signature_valid,
|
||||
status: value.status,
|
||||
payload: value.payload,
|
||||
error_message: value.error_message,
|
||||
created_at_unix_secs: value.created_at_unix_secs,
|
||||
processed_at_unix_secs: value.processed_at_unix_secs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::{AppState, GatewayError};
|
||||
|
||||
use crate::{async_task, video_tasks};
|
||||
use aether_data_contracts::repository::video_tasks::{
|
||||
StoredVideoTask, UpsertVideoTask, VideoTaskLookupKey, VideoTaskModelCount,
|
||||
VideoTaskQueryFilter, VideoTaskStatusCount,
|
||||
};
|
||||
|
||||
impl AppState {
|
||||
pub(crate) async fn read_data_backed_video_task_response(
|
||||
@@ -17,9 +21,9 @@ impl AppState {
|
||||
pub(crate) async fn find_video_task_by_id(
|
||||
&self,
|
||||
task_id: &str,
|
||||
) -> Result<Option<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
) -> Result<Option<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.find_video_task(aether_data::repository::video_tasks::VideoTaskLookupKey::Id(task_id))
|
||||
.find_video_task(VideoTaskLookupKey::Id(task_id))
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
@@ -27,11 +31,9 @@ impl AppState {
|
||||
pub(crate) async fn find_video_task_by_short_id(
|
||||
&self,
|
||||
short_id: &str,
|
||||
) -> Result<Option<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
) -> Result<Option<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.find_video_task(
|
||||
aether_data::repository::video_tasks::VideoTaskLookupKey::ShortId(short_id),
|
||||
)
|
||||
.find_video_task(VideoTaskLookupKey::ShortId(short_id))
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))
|
||||
}
|
||||
@@ -39,7 +41,7 @@ impl AppState {
|
||||
pub(crate) async fn upsert_video_task_snapshot(
|
||||
&self,
|
||||
snapshot: &video_tasks::LocalVideoTaskSnapshot,
|
||||
) -> Result<Option<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
) -> Result<Option<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.upsert_video_task(snapshot.to_upsert_record())
|
||||
.await
|
||||
@@ -77,7 +79,7 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn reconstruct_video_task_snapshot(
|
||||
&self,
|
||||
task: &aether_data::repository::video_tasks::StoredVideoTask,
|
||||
task: &StoredVideoTask,
|
||||
) -> Result<Option<video_tasks::LocalVideoTaskSnapshot>, GatewayError> {
|
||||
crate::provider_transport::reconstruct_local_video_task_snapshot(self, task)
|
||||
.await
|
||||
@@ -89,7 +91,7 @@ impl AppState {
|
||||
now_unix_secs: u64,
|
||||
claim_until_unix_secs: u64,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
) -> Result<Vec<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.claim_due_video_tasks(now_unix_secs, claim_until_unix_secs, limit)
|
||||
.await
|
||||
@@ -98,8 +100,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn update_active_video_task(
|
||||
&self,
|
||||
task: aether_data::repository::video_tasks::UpsertVideoTask,
|
||||
) -> Result<Option<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
task: UpsertVideoTask,
|
||||
) -> Result<Option<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.update_active_video_task(task)
|
||||
.await
|
||||
@@ -108,10 +110,10 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn list_video_task_page(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
filter: &VideoTaskQueryFilter,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::video_tasks::StoredVideoTask>, GatewayError> {
|
||||
) -> Result<Vec<StoredVideoTask>, GatewayError> {
|
||||
self.data
|
||||
.list_video_task_page(filter, offset, limit)
|
||||
.await
|
||||
@@ -120,7 +122,7 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn count_video_tasks(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
filter: &VideoTaskQueryFilter,
|
||||
) -> Result<u64, GatewayError> {
|
||||
self.data
|
||||
.count_video_tasks(filter)
|
||||
@@ -130,8 +132,8 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn count_video_tasks_by_status(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
) -> Result<Vec<aether_data::repository::video_tasks::VideoTaskStatusCount>, GatewayError> {
|
||||
filter: &VideoTaskQueryFilter,
|
||||
) -> Result<Vec<VideoTaskStatusCount>, GatewayError> {
|
||||
self.data
|
||||
.count_video_tasks_by_status(filter)
|
||||
.await
|
||||
@@ -140,7 +142,7 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn count_distinct_video_task_users(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
filter: &VideoTaskQueryFilter,
|
||||
) -> Result<u64, GatewayError> {
|
||||
self.data
|
||||
.count_distinct_video_task_users(filter)
|
||||
@@ -150,9 +152,9 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn top_video_task_models(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
filter: &VideoTaskQueryFilter,
|
||||
limit: usize,
|
||||
) -> Result<Vec<aether_data::repository::video_tasks::VideoTaskModelCount>, GatewayError> {
|
||||
) -> Result<Vec<VideoTaskModelCount>, GatewayError> {
|
||||
self.data
|
||||
.top_video_task_models(filter, limit)
|
||||
.await
|
||||
@@ -161,7 +163,7 @@ impl AppState {
|
||||
|
||||
pub(crate) async fn count_video_tasks_created_since(
|
||||
&self,
|
||||
filter: &aether_data::repository::video_tasks::VideoTaskQueryFilter,
|
||||
filter: &VideoTaskQueryFilter,
|
||||
created_since_unix_secs: u64,
|
||||
) -> Result<u64, GatewayError> {
|
||||
self.data
|
||||
|
||||
Reference in New Issue
Block a user