Skip to main content

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}