boxxkite_client/preview.rs
1//! Network-ingress preview URLs (`docs/NETWORK-INGRESS-DESIGN.md`):
2//! `POST /v1/sandboxes/{id}/preview/{port}` and its `/revoke` sibling.
3//! Mirrors `sdk-python`'s `create_preview_url`/`revoke_preview_url` and
4//! `sdk-go`'s `CreatePreviewURL`/`RevokePreviewURL`.
5
6use reqwest::Method;
7use serde::{Deserialize, Serialize};
8
9use crate::client::Client;
10use crate::error::BoxxkiteError;
11
12/// A signed, time-limited preview URL (`POST /v1/sandboxes/{id}/preview/{port}`'s
13/// response). The URL carries its own authorization -- no API key is needed
14/// to use it, only to mint it.
15#[derive(Debug, Clone, Deserialize)]
16pub struct PreviewUrl {
17 pub url: String,
18 pub expires_at: String,
19 pub token_id: String,
20}
21
22/// `POST /v1/sandboxes/{id}/preview/{port}/revoke`'s response.
23#[derive(Debug, Clone, Deserialize)]
24pub struct PreviewRevokeResult {
25 pub revoked: bool,
26 pub token_id: String,
27}
28
29impl Client {
30 /// `POST /v1/sandboxes/{session_id}/preview/{port}` -- mint a signed,
31 /// time-limited URL that proxies HTTP traffic to a port a background
32 /// process opened inside this session (see
33 /// [`StartProcessOptions::expose_port`](crate::StartProcessOptions::expose_port)).
34 ///
35 /// `ttl_seconds` bounds how long the minted URL stays valid (30-86400);
36 /// defaults to 900 (15 minutes) server-side when `None`.
37 pub async fn create_preview_url(
38 &self,
39 session_id: &str,
40 port: u16,
41 ttl_seconds: Option<u32>,
42 ) -> Result<PreviewUrl, BoxxkiteError> {
43 #[derive(Serialize)]
44 struct Body {
45 #[serde(skip_serializing_if = "Option::is_none")]
46 ttl_seconds: Option<u32>,
47 }
48 let builder = self
49 .request(
50 Method::POST,
51 &format!("/v1/sandboxes/{session_id}/preview/{port}"),
52 )
53 .json(&Body { ttl_seconds });
54 self.send(builder).await
55 }
56
57 /// `POST /v1/sandboxes/{session_id}/preview/{port}/revoke` -- invalidate
58 /// one specific preview-URL token (its `token_id` from
59 /// [`Client::create_preview_url`]) before its TTL expires, without
60 /// tearing down the session and without affecting any other token minted
61 /// for the same session/port. Idempotent: revoking an already-revoked,
62 /// already-expired, or unrecognized `token_id` still returns
63 /// `revoked = true` rather than erroring.
64 pub async fn revoke_preview_url(
65 &self,
66 session_id: &str,
67 port: u16,
68 token_id: &str,
69 ) -> Result<PreviewRevokeResult, BoxxkiteError> {
70 #[derive(Serialize)]
71 struct Body<'a> {
72 token_id: &'a str,
73 }
74 let builder = self
75 .request(
76 Method::POST,
77 &format!("/v1/sandboxes/{session_id}/preview/{port}/revoke"),
78 )
79 .json(&Body { token_id });
80 self.send(builder).await
81 }
82}