Added federation
Build and Publish / Build and Publish Docker Image (push) Successful in 4m55s

This commit is contained in:
Ultradesu
2026-07-17 13:30:55 +03:00
parent 184371afca
commit 0615356785
11 changed files with 3832 additions and 489 deletions
+32
View File
@@ -415,6 +415,38 @@ impl App for AdminApp {
}),
"admin_v2_settings_probe",
),
Route::with_handler_and_name(
"/v2/api/federation",
get(move |session: Session, db: Database| async move {
v2::federation_status(session, db).await
}),
"admin_v2_federation_status",
),
Route::with_handler_and_name(
"/v2/api/federation/sync",
cot::router::method::post(move |session: Session, db: Database| async move {
v2::federation_sync(session, db).await
}),
"admin_v2_federation_sync",
),
Route::with_handler_and_name(
"/v2/api/federation/ticket",
get(move |session: Session, db: Database| async move {
v2::federation_ticket(session, db).await
}),
"admin_v2_federation_ticket",
),
Route::with_handler_and_name(
"/v2/api/federation/connect",
cot::router::method::post(
move |session: Session,
db: Database,
json: Json<v2::FederationConnectRequest>| async move {
v2::federation_connect(session, db, json).await
},
),
"admin_v2_federation_connect",
),
Route::with_handler_and_name(
"/v2/api/jobs/{name}/toggle",
cot::router::method::post({
+88
View File
@@ -448,6 +448,10 @@ struct AdminSettingsValues {
agent_confidence_threshold: String,
agent_context_limit: String,
agent_concurrency: String,
#[serde(default)]
federation_enabled: bool,
#[serde(default)]
federation_network_id: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
@@ -472,6 +476,8 @@ struct AdminSettingsSources {
agent_confidence_threshold: &'static str,
agent_context_limit: &'static str,
agent_concurrency: &'static str,
federation_enabled: &'static str,
federation_network_id: &'static str,
}
#[derive(Debug, Deserialize)]
@@ -496,6 +502,10 @@ pub(super) struct UpdateSettingsRequest {
agent_confidence_threshold: String,
agent_context_limit: String,
agent_concurrency: String,
#[serde(default)]
federation_enabled: bool,
#[serde(default)]
federation_network_id: String,
}
#[derive(Debug, Serialize, JsonSchema)]
@@ -978,6 +988,11 @@ pub async fn update_settings(
"agent_concurrency",
body.agent_concurrency.trim().to_string(),
),
("federation_enabled", body.federation_enabled.to_string()),
(
"federation_network_id",
body.federation_network_id.trim().to_string(),
),
];
for (key, value) in fields {
let mut entry = ConfigEntry::new(key.to_string(), value);
@@ -986,9 +1001,78 @@ pub async fn update_settings(
.await
.map_err(|e| cot::Error::internal(e.to_string()))?;
}
// Federation applies on the fly: (re)start or stop the node to match
// the freshly saved settings — no server restart involved.
let (fresh, _) = AppConfig::load_with_db(&db).await;
tokio::spawn(async move {
crate::federation::handle().apply(&fresh).await;
});
Json(serde_json::json!({ "ok": true })).into_response()
}
// ---------------------------------------------------------------------------
// Federation (status + manual controls)
// ---------------------------------------------------------------------------
pub async fn federation_status(
session: Session,
db: Database,
) -> cot::Result<cot::response::Response> {
if let Err(response) = require_admin_json(&session, &db).await {
return Ok(response);
}
Json(crate::federation::handle().status().await).into_response()
}
pub async fn federation_sync(
session: Session,
db: Database,
) -> cot::Result<cot::response::Response> {
if let Err(response) = require_admin_json(&session, &db).await {
return Ok(response);
}
let fed = crate::federation::handle();
if let Err(err) = fed.sync_now().await {
return Ok(json_error(
StatusCode::BAD_REQUEST,
&format!("sync failed: {err:#}"),
));
}
Json(fed.status().await).into_response()
}
pub async fn federation_ticket(
session: Session,
db: Database,
) -> cot::Result<cot::response::Response> {
if let Err(response) = require_admin_json(&session, &db).await {
return Ok(response);
}
match crate::federation::handle().ticket().await {
Ok(ticket) => Json(serde_json::json!({ "ticket": ticket })).into_response(),
Err(err) => Ok(json_error(StatusCode::BAD_REQUEST, &format!("{err:#}"))),
}
}
#[derive(Debug, Deserialize, JsonSchema)]
pub(super) struct FederationConnectRequest {
ticket: String,
}
pub async fn federation_connect(
session: Session,
db: Database,
Json(body): Json<FederationConnectRequest>,
) -> cot::Result<cot::response::Response> {
if let Err(response) = require_admin_json(&session, &db).await {
return Ok(response);
}
match crate::federation::handle().connect(&body.ticket).await {
Ok(peer) => Json(serde_json::json!({ "connected": peer })).into_response(),
Err(err) => Ok(json_error(StatusCode::BAD_REQUEST, &format!("{err:#}"))),
}
}
pub async fn settings_probe(
session: Session,
db: Database,
@@ -1057,6 +1141,8 @@ fn settings_dto(config: AppConfig, sources: ConfigSources) -> AdminSettingsDto {
agent_confidence_threshold: config.agent_confidence_threshold.to_string(),
agent_context_limit: config.agent_context_limit.to_string(),
agent_concurrency: config.agent_concurrency.to_string(),
federation_enabled: config.federation_enabled,
federation_network_id: config.federation_network_id,
},
sources: AdminSettingsSources {
auth_password_enabled: sources.auth_password_enabled.code(),
@@ -1079,6 +1165,8 @@ fn settings_dto(config: AppConfig, sources: ConfigSources) -> AdminSettingsDto {
agent_confidence_threshold: sources.agent_confidence_threshold.code(),
agent_context_limit: sources.agent_context_limit.code(),
agent_concurrency: sources.agent_concurrency.code(),
federation_enabled: sources.federation_enabled.code(),
federation_network_id: sources.federation_network_id.code(),
},
}
}