Skip to content

Commit 08f4697

Browse files
committed
Finally, a working version
1 parent 22e1600 commit 08f4697

File tree

18 files changed

+698
-415
lines changed

18 files changed

+698
-415
lines changed

Cargo.lock

Lines changed: 354 additions & 339 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ ipinfo = { git = "https://github.com/rakshith-ravi/ipinfo-rust", branch = "featu
5757
ipnetwork = { version = "0.20", default-features = false }
5858
jsonwebtoken = { version = "9", default-features = false }
5959
k8s-openapi = { version = "0.23", default-features = false }
60-
kube = { version = "0.95", default-features = false }
60+
kube = { version = "0.96", default-features = false }
6161
leptos = { version = "0.6", default-features = false }
6262
leptos-use = { version = "0.13", default-features = false }
6363
leptos_axum = { version = "0.6", default-features = false }
@@ -71,9 +71,9 @@ matchit = { version = "0.7", default-features = false }
7171
models = { path = "models", default-features = false }
7272
monostate = { version = "0.1", default-features = false }
7373
open = { version = "5", default-features = false }
74-
opentelemetry = { version = "0.25", default-features = false }
75-
opentelemetry-otlp = { version = "0.25", default-features = false }
76-
opentelemetry_sdk = { version = "0.25", default-features = false }
74+
opentelemetry = { version = "0.26", default-features = false }
75+
opentelemetry-otlp = { version = "0.26", default-features = false }
76+
opentelemetry_sdk = { version = "0.26", default-features = false }
7777
preprocess = { version = "0.5", default-features = false }
7878
proc-macro2 = { version = "1", default-features = false }
7979
quote = { version = "1", default-features = false }
@@ -98,11 +98,11 @@ tokio = { version = "1", default-features = false }
9898
tokio-stream = { version = "0.1", default-features = false }
9999
tokio-tungstenite = { version = "0.24", default-features = false }
100100
totp-rs = { version = "5", default-features = false }
101-
tower = { version = "0.4", default-features = false }
102-
tower-http = { version = "0.5", default-features = false }
101+
tower = { version = "0.5", default-features = false }
102+
tower-http = { version = "0.6", default-features = false }
103103
tracing = { version = "0.1", default-features = false }
104104
tracing-log = { version = "0.2", default-features = false }
105-
tracing-opentelemetry = { version = "0.26", default-features = false }
105+
tracing-opentelemetry = { version = "0.27", default-features = false }
106106
tracing-subscriber = { version = "0.3", default-features = false }
107107
typed-builder = { version = "0.20", default-features = false }
108108
url = { version = "2", default-features = false }

api/src/routes/api.patr.cloud/workspace/deployment/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ pub async fn setup_routes(state: &AppState) -> Router {
5353
.mount_auth_endpoint(delete_deployment, state)
5454
.mount_auth_endpoint(update_deployment, state)
5555
.mount_auth_endpoint(get_deployment_metric, state)
56-
.mount_auth_endpoint(stream_deployment_logs, state)
56+
.mount_auth_stream(stream_deployment_logs, state)
5757
}

api/src/routes/api.patr.cloud/workspace/deployment/start_deployment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub async fn start_deployment(
3434

3535
let now = OffsetDateTime::now_utc();
3636

37-
let (registry, image_tag, region) = query!(
37+
let (registry, ..) = query!(
3838
r#"
3939
SELECT
4040
registry,

api/src/routes/api.patr.cloud/workspace/runner/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use self::{
1919
#[instrument(skip(state))]
2020
pub async fn setup_routes(state: &AppState) -> Router {
2121
Router::new()
22-
.mount_auth_endpoint(stream_runner_data_for_workspace, state)
22+
.mount_auth_stream(stream_runner_data_for_workspace, state)
2323
.mount_auth_endpoint(add_runner_to_workspace, state)
2424
.mount_auth_endpoint(remove_runner_from_workspace, state)
2525
.mount_auth_endpoint(list_runners_for_workspace, state)

api/src/utils/router_ext.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use models::{
1010
ApiRequest,
1111
};
1212
use preprocess::Preprocessable;
13+
use serde::{de::DeserializeOwned, Serialize};
1314
use tower::{
1415
util::{BoxCloneService, BoxLayer},
1516
ServiceBuilder,
@@ -44,6 +45,17 @@ where
4445
/// Rate limiter using tower layers.
4546
#[track_caller]
4647
fn mount_endpoint<E, H>(self, handler: H, state: &AppState) -> Self
48+
where
49+
for<'req> H: EndpointHandler<'req, E> + Clone + Send + Sync + 'static,
50+
E: ApiEndpoint<Authenticator = NoAuthentication> + Sync,
51+
<E::RequestBody as Preprocessable>::Processed: Send,
52+
E::RequestBody: Serialize + DeserializeOwned,
53+
E::ResponseBody: Serialize + DeserializeOwned;
54+
55+
/// Mount an API stream endpoint directly along with the required request
56+
/// parser, Rate limiter using tower layers.
57+
#[track_caller]
58+
fn mount_stream<E, H>(self, handler: H, state: &AppState) -> Self
4759
where
4860
for<'req> H: EndpointHandler<'req, E> + Clone + Send + Sync + 'static,
4961
E: ApiEndpoint<Authenticator = NoAuthentication> + Sync,
@@ -53,6 +65,19 @@ where
5365
/// Rate limiter, Audit logger and Auth middlewares, using tower layers.
5466
#[track_caller]
5567
fn mount_auth_endpoint<E, H>(self, handler: H, state: &AppState) -> Self
68+
where
69+
for<'req> H: AuthEndpointHandler<'req, E> + Clone + Send + Sync + 'static,
70+
E: ApiEndpoint<Authenticator = AppAuthentication<E>> + Sync,
71+
<E::RequestBody as Preprocessable>::Processed: Send,
72+
E::RequestBody: Serialize + DeserializeOwned,
73+
E::ResponseBody: Serialize + DeserializeOwned,
74+
E::RequestHeaders: HasHeader<BearerToken>;
75+
76+
/// Mount an API stream endpoint directly along with the required request
77+
/// parser, Rate limiter, Audit logger and Auth middlewares, using tower
78+
/// layers.
79+
#[track_caller]
80+
fn mount_auth_stream<E, H>(self, handler: H, state: &AppState) -> Self
5681
where
5782
for<'req> H: AuthEndpointHandler<'req, E> + Clone + Send + Sync + 'static,
5883
E: ApiEndpoint<Authenticator = AppAuthentication<E>> + Sync,
@@ -66,6 +91,73 @@ where
6691
{
6792
#[instrument(skip_all)]
6893
fn mount_endpoint<E, H>(self, handler: H, state: &AppState) -> Self
94+
where
95+
for<'req> H: EndpointHandler<'req, E> + Clone + Send + Sync + 'static,
96+
E: ApiEndpoint<Authenticator = NoAuthentication> + Sync,
97+
<E::RequestBody as Preprocessable>::Processed: Send,
98+
E::RequestBody: Serialize + DeserializeOwned,
99+
E::ResponseBody: Serialize + DeserializeOwned,
100+
{
101+
frontend::utils::API_CALL_REGISTRY
102+
.get_or_init(|| RwLock::new(Default::default()))
103+
.write()
104+
.expect("API call registry poisoned")
105+
.entry(E::METHOD)
106+
.or_default()
107+
.insert(
108+
<E::RequestPath as TypedPath>::PATH,
109+
Box::new(BoxLayer::<
110+
BoxCloneService<(ApiRequest<E>, IpAddr), AppResponse<E>, ErrorType>,
111+
(ApiRequest<E>, IpAddr),
112+
AppResponse<E>,
113+
ErrorType,
114+
>::new(
115+
ServiceBuilder::new()
116+
// .layer(todo!("Add rate limiter checker middleware here")),
117+
.layer(DataStoreConnectionLayer::<E>::with_state(state.clone()))
118+
.layer(PreprocessLayer::new())
119+
.layer(UserAgentValidationLayer::new())
120+
// .layer(todo!("Add rate limiter value updater middleware here"))
121+
.layer(EndpointLayer::new(handler.clone())),
122+
)),
123+
)
124+
.unwrap_or_else(|_| {
125+
panic!(
126+
"API endpoint `{} {}` already registered",
127+
E::METHOD,
128+
<E::RequestPath as TypedPath>::PATH
129+
);
130+
});
131+
132+
frontend::utils::register_api_call::<E>();
133+
134+
// Setup the layers for the backend
135+
if <E as ApiEndpoint>::API_ALLOWED || cfg!(debug_assertions) {
136+
self.route(
137+
<<E as ApiEndpoint>::RequestPath as TypedPath>::PATH,
138+
MethodRouter::<S>::new()
139+
.on(
140+
MethodFilter::try_from(<E as ApiEndpoint>::METHOD).unwrap(),
141+
|| async {},
142+
)
143+
.layer(
144+
ServiceBuilder::new()
145+
// .layer(todo!("Add rate limiter checker middleware here")),
146+
.layer(RequestParserLayer::new())
147+
.layer(DataStoreConnectionLayer::with_state(state.clone()))
148+
// .layer(todo!("Add rate limiter value updater middleware here"))
149+
.layer(PreprocessLayer::new())
150+
.layer(UserAgentValidationLayer::new())
151+
.layer(EndpointLayer::new(handler)),
152+
),
153+
)
154+
} else {
155+
self
156+
}
157+
}
158+
159+
#[instrument(skip_all)]
160+
fn mount_stream<E, H>(self, handler: H, state: &AppState) -> Self
69161
where
70162
for<'req> H: EndpointHandler<'req, E> + Clone + Send + Sync + 'static,
71163
E: ApiEndpoint<Authenticator = NoAuthentication> + Sync,
@@ -129,6 +221,80 @@ where
129221

130222
#[instrument(skip_all)]
131223
fn mount_auth_endpoint<E, H>(self, handler: H, state: &AppState) -> Self
224+
where
225+
for<'req> H: AuthEndpointHandler<'req, E> + Clone + Send + Sync + 'static,
226+
E: ApiEndpoint<Authenticator = AppAuthentication<E>> + Sync,
227+
<E::RequestBody as Preprocessable>::Processed: Send,
228+
E::RequestBody: Serialize + DeserializeOwned,
229+
E::ResponseBody: Serialize + DeserializeOwned,
230+
E::RequestHeaders: HasHeader<BearerToken>,
231+
{
232+
frontend::utils::API_CALL_REGISTRY
233+
.get_or_init(|| RwLock::new(Default::default()))
234+
.write()
235+
.expect("API call registry poisoned")
236+
.entry(E::METHOD)
237+
.or_default()
238+
.insert(
239+
<E::RequestPath as TypedPath>::PATH,
240+
Box::new(BoxLayer::<
241+
BoxCloneService<(ApiRequest<E>, IpAddr), AppResponse<E>, ErrorType>,
242+
(ApiRequest<E>, IpAddr),
243+
AppResponse<E>,
244+
ErrorType,
245+
>::new(
246+
ServiceBuilder::new()
247+
// .layer(todo!("Add rate limiter checker middleware here")),
248+
.layer(DataStoreConnectionLayer::with_state(state.clone()))
249+
.layer(PreprocessLayer::new())
250+
.layer(UserAgentValidationLayer::new())
251+
.layer(AuthenticationLayer::new(ClientType::WebDashboard))
252+
// .layer(todo!("Add permission checker middleware here"))
253+
// .layer(todo!("Add rate limiter value updater middleware here"))
254+
// .layer(todo!("Add audit logger middleware here"))
255+
.layer(AuthEndpointLayer::new(handler.clone())),
256+
)),
257+
)
258+
.unwrap_or_else(|_| {
259+
panic!(
260+
"API endpoint `{} {}` already registered",
261+
E::METHOD,
262+
<E::RequestPath as TypedPath>::PATH
263+
);
264+
});
265+
266+
frontend::utils::register_api_call::<E>();
267+
268+
// Setup the layers for the backend
269+
if <E as ApiEndpoint>::API_ALLOWED || cfg!(debug_assertions) {
270+
self.route(
271+
<<E as ApiEndpoint>::RequestPath as TypedPath>::PATH,
272+
MethodRouter::<S>::new()
273+
.on(
274+
MethodFilter::try_from(<E as ApiEndpoint>::METHOD).unwrap(),
275+
|| async {},
276+
)
277+
.layer(
278+
ServiceBuilder::new()
279+
// .layer(todo!("Add rate limiter checker middleware here")),
280+
.layer(RequestParserLayer::new())
281+
.layer(DataStoreConnectionLayer::with_state(state.clone()))
282+
.layer(PreprocessLayer::new())
283+
.layer(UserAgentValidationLayer::new())
284+
.layer(AuthenticationLayer::new(ClientType::ApiToken))
285+
// .layer(todo!("Add permission checker middleware here"))
286+
// .layer(todo!("Add rate limiter value updater middleware here"))
287+
// .layer(todo!("Add audit logger middleware here"))
288+
.layer(AuthEndpointLayer::new(handler)),
289+
),
290+
)
291+
} else {
292+
self
293+
}
294+
}
295+
296+
#[instrument(skip_all)]
297+
fn mount_auth_stream<E, H>(self, handler: H, state: &AppState) -> Self
132298
where
133299
for<'req> H: AuthEndpointHandler<'req, E> + Clone + Send + Sync + 'static,
134300
E: ApiEndpoint<Authenticator = AppAuthentication<E>> + Sync,

frontend/src/api/workspace/deployment/list.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use std::{thread, time};
2-
31
use models::api::workspace::deployment::*;
4-
use server_fn::codec::FromRes;
52

63
use crate::prelude::*;
74

@@ -15,7 +12,6 @@ pub async fn list_deployments(
1512
) -> Result<(usize, ListDeploymentResponse), ServerFnError<ErrorType>> {
1613
use std::str::FromStr;
1714

18-
thread::sleep(time::Duration::from_secs(2));
1915
let access_token = BearerToken::from_str(access_token.unwrap().as_str())
2016
.map_err(|_| ServerFnError::WrappedServerError(ErrorType::MalformedAccessToken))?;
2117

frontend/src/pages/infrastructure/deployment/create_deployment/running.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::str::FromStr;
22

3-
use ev::MouseEvent;
43
use models::api::workspace::deployment::{EnvironmentVariableValue, ExposedPortType};
54

65
use super::{super::components::*, RunnerPageError};

frontend/src/pages/infrastructure/deployment/dashboard/footer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::rc::Rc;
22

33
use ev::MouseEvent;
4-
use leptos_query::QueryResult;
54

65
use crate::prelude::*;
76

frontend/src/pages/infrastructure/deployment/dashboard/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use convert_case::*;
55
use leptos_query::QueryResult;
66

77
use self::{footer::*, head::*};
8-
use super::{components::*, utils::*};
8+
use super::components::*;
99
use crate::{
1010
prelude::*,
1111
queries::{list_deployments_query, AllDeploymentsTag},

0 commit comments

Comments
 (0)