Skip to content

Commit 6f8681c

Browse files
nipunn1313Convex, Inc.
authored and
Convex, Inc.
committed
Make more public export endpoints for /set_expiration and /cancel (#37232)
Will be handy for self-hosted. At least gives people something to curl. GitOrigin-RevId: 6184ba01e9023b0a052e7940bb30aee815c4035d
1 parent b9b37f2 commit 6f8681c

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

crates/common/src/types/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod functions;
2525
mod index;
2626
mod maybe_value;
2727
mod object_key;
28+
mod snapshot_export;
2829
mod table;
2930
mod timestamp;
3031

@@ -87,6 +88,7 @@ pub use object_key::{
8788
FullyQualifiedObjectKey,
8889
ObjectKey,
8990
};
91+
pub use snapshot_export::SetExportExpirationRequest;
9092
pub use table::TableStats;
9193
#[cfg(any(test, feature = "testing"))]
9294
pub use timestamp::unchecked_repeatable_ts;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use serde::{
2+
Deserialize,
3+
Serialize,
4+
};
5+
6+
#[derive(Serialize, Deserialize, Debug)]
7+
#[serde(rename_all = "camelCase")]
8+
pub struct SetExportExpirationRequest {
9+
pub expiration_ts_ns: u64,
10+
}

crates/local_backend/src/router.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ use crate::{
110110
schema_state,
111111
},
112112
snapshot_export::{
113+
cancel_export,
113114
get_zip_export,
114115
request_zip_export,
116+
set_export_expiration,
115117
},
116118
snapshot_import::{
117119
cancel_import,
@@ -202,7 +204,9 @@ pub fn router(st: LocalAppState) -> Router {
202204

203205
let snapshot_export_routes = Router::new()
204206
.route("/request/zip", post(request_zip_export))
205-
.route("/zip/{id}", get(get_zip_export));
207+
.route("/zip/{id}", get(get_zip_export))
208+
.route("/set_expiration/{snapshot_id}", post(set_export_expiration))
209+
.route("/cancel/{snapshot_id}", post(cancel_export));
206210

207211
let api_routes = Router::new()
208212
.merge(cli_routes)

crates/local_backend/src/snapshot_export.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@ use common::{
1818
components::ComponentId,
1919
http::{
2020
extract::{
21+
Json,
2122
Path,
2223
Query,
2324
},
2425
HttpResponseError,
2526
},
27+
types::SetExportExpirationRequest,
2628
};
2729
use either::Either;
2830
use errors::ErrorMetadata;
2931
use http::StatusCode;
30-
use model::exports::types::{
31-
ExportFormat,
32-
ExportRequestor,
32+
use model::exports::{
33+
types::{
34+
ExportFormat,
35+
ExportRequestor,
36+
},
37+
ExportsModel,
3338
};
3439
use serde::Deserialize;
3540
use storage::StorageGetStream;
@@ -118,3 +123,56 @@ pub async fn get_zip_export(
118123
Body::from_stream(stream),
119124
))
120125
}
126+
127+
#[derive(Deserialize)]
128+
pub struct SetExportExpirationPathArgs {
129+
snapshot_id: String,
130+
}
131+
132+
#[debug_handler]
133+
#[fastrace::trace]
134+
pub async fn set_export_expiration(
135+
State(st): State<LocalAppState>,
136+
ExtractIdentity(identity): ExtractIdentity,
137+
Path(SetExportExpirationPathArgs { snapshot_id }): Path<SetExportExpirationPathArgs>,
138+
Json(SetExportExpirationRequest { expiration_ts_ns }): Json<SetExportExpirationRequest>,
139+
) -> Result<StatusCode, HttpResponseError> {
140+
if !(identity.is_system() || identity.is_admin()) {
141+
Err(anyhow::anyhow!(ErrorMetadata::forbidden(
142+
"SetExportExpirationForbidden",
143+
"Must have system or admin identity to set export expiration"
144+
)))?;
145+
}
146+
let snapshot_id: DeveloperDocumentId = snapshot_id
147+
.parse::<DeveloperDocumentId>()
148+
.map_err(|e| anyhow::anyhow!(e))?;
149+
let mut tx = st.application.begin(identity).await?;
150+
ExportsModel::new(&mut tx)
151+
.set_expiration(snapshot_id, expiration_ts_ns)
152+
.await?;
153+
st.application.commit(tx, "set_export_expiration").await?;
154+
Ok(StatusCode::OK)
155+
}
156+
157+
#[debug_handler]
158+
#[fastrace::trace]
159+
pub async fn cancel_export(
160+
State(st): State<LocalAppState>,
161+
ExtractIdentity(identity): ExtractIdentity,
162+
Path(SetExportExpirationPathArgs { snapshot_id }): Path<SetExportExpirationPathArgs>,
163+
) -> Result<StatusCode, HttpResponseError> {
164+
// This route is accessed directly from the admin dashboard
165+
if !(identity.is_system() || identity.is_admin()) {
166+
Err(anyhow::anyhow!(ErrorMetadata::forbidden(
167+
"CancelExportForbidden",
168+
"Must have system or admin identity to cancel cloud export"
169+
)))?;
170+
}
171+
let snapshot_id: DeveloperDocumentId = snapshot_id
172+
.parse::<DeveloperDocumentId>()
173+
.map_err(|e| anyhow::anyhow!(e))?;
174+
let mut tx = st.application.begin(identity).await?;
175+
ExportsModel::new(&mut tx).cancel(snapshot_id).await?;
176+
st.application.commit(tx, "cancel_export").await?;
177+
Ok(StatusCode::OK)
178+
}

0 commit comments

Comments
 (0)