feat(deploy-to-hub): thin /triggers proxy to forward trigger bulk-sync to Hub

Mirrors the existing /scripts, /flows, /apps thin proxies. Forwards
{ triggers, workspace_slug } to Hub's POST /workspaces/[slug]/triggers
bulk-replace endpoint, with the same require_admin + HUB_DEV_TOKEN
guardrails. Lets the frontend push trigger stubs in a single round-trip
after the items they reference have landed on the Hub.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
tristantr
2026-06-08 11:35:47 +02:00
co-authored by Claude Opus 4.7
parent 16eae212cc
commit c4c85db64b
+35
View File
@@ -24,6 +24,7 @@ pub fn workspaced_service() -> Router {
.route("/flows/{flow_id}/recording", post(publish_flow_recording))
.route("/resource_types", post(publish_resource_type))
.route("/resources", post(publish_resources))
.route("/triggers", post(publish_triggers))
}
#[derive(Deserialize)]
@@ -235,6 +236,40 @@ async fn publish_resources(
.await
}
#[derive(Deserialize, Serialize)]
struct PublishTriggerBody {
path: String,
kind: String,
#[serde(skip_serializing_if = "Option::is_none")]
summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
config: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
script_ask_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
flow_id: Option<i64>,
}
#[derive(Deserialize, Serialize)]
struct PublishTriggersBody {
triggers: Vec<PublishTriggerBody>,
workspace_slug: String,
}
async fn publish_triggers(
authed: ApiAuthed,
Path(_workspace): Path<String>,
Json(body): Json<PublishTriggersBody>,
) -> Result<impl IntoResponse, Error> {
require_admin(authed.is_admin, &authed.username)?;
forward_to_hub(
&format!("/workspaces/{}/triggers", body.workspace_slug),
&body,
)
.await
}
async fn forward_to_hub<T: Serialize>(path: &str, body: &T) -> Result<(StatusCode, String), Error> {
let hub_token = std::env::var("HUB_DEV_TOKEN")
.map_err(|_| Error::BadRequest("HUB_DEV_TOKEN not set".into()))?;