@browsersync/bslive 0.0.9 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +3 -2
- package/bslive/src/lib.rs +8 -3
- package/bsnext/src/main.rs +9 -3
- package/crates/bsnext_client/generated/dto.ts +33 -2
- package/crates/bsnext_client/generated/schema.ts +52 -2
- package/crates/bsnext_client/{dist → inject/dist}/index.js +105 -58
- package/crates/bsnext_client/inject/package.json +12 -0
- package/crates/bsnext_client/{ts → inject/src}/index.ts +2 -2
- package/crates/bsnext_client/package-lock.json +89 -3
- package/crates/bsnext_client/package.json +4 -2
- package/crates/bsnext_client/src/lib.rs +10 -0
- package/crates/bsnext_client/tsconfig.json +8 -2
- package/crates/bsnext_client/ui/dist/index.css +78 -0
- package/crates/bsnext_client/ui/dist/index.js +997 -0
- package/crates/bsnext_client/{index.html → ui/index.html} +8 -4
- package/crates/bsnext_client/ui/input.yml +19 -0
- package/crates/bsnext_client/ui/package.json +18 -0
- package/crates/bsnext_client/ui/src/components/bs-debug.ts +27 -0
- package/crates/bsnext_client/ui/src/components/bs-header.ts +33 -0
- package/crates/bsnext_client/ui/src/components/bs-icon.ts +101 -0
- package/crates/bsnext_client/ui/src/components/bs-server-detail.ts +21 -0
- package/crates/bsnext_client/ui/src/components/bs-server-identity.ts +24 -0
- package/crates/bsnext_client/ui/src/components/bs-server-list.ts +39 -0
- package/crates/bsnext_client/ui/src/index.ts +39 -0
- package/crates/bsnext_client/ui/styles/base.css.ts +17 -0
- package/crates/bsnext_client/ui/styles/reset.css +52 -0
- package/crates/bsnext_client/ui/styles/style.css +29 -0
- package/crates/bsnext_client/ui/svg/wordmark-white.svg +38 -0
- package/crates/bsnext_core/Cargo.toml +1 -0
- package/crates/bsnext_core/src/common_layers.rs +62 -0
- package/crates/bsnext_core/src/dir_loader.rs +1 -1
- package/crates/bsnext_core/src/dto.rs +62 -2
- package/crates/bsnext_core/src/lib.rs +1 -0
- package/crates/bsnext_core/src/not_found/mod.rs +0 -1
- package/crates/bsnext_core/src/not_found/not_found_service.rs +5 -11
- package/crates/bsnext_core/src/raw_loader.rs +4 -34
- package/crates/bsnext_core/src/server/handler_listen.rs +10 -4
- package/crates/bsnext_core/src/server/router/assets.rs +39 -0
- package/crates/bsnext_core/src/server/router/mod.rs +15 -4
- package/crates/bsnext_core/src/server/router/pub_api.rs +39 -0
- package/crates/bsnext_core/src/server/router/snapshots/bsnext_core__server__router__tests__test__handlers.snap +9 -0
- package/crates/bsnext_core/src/server/router/tests.rs +11 -6
- package/crates/bsnext_core/src/server/state.rs +4 -0
- package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +3 -2
- package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +5 -1
- package/crates/bsnext_fs/src/actor.rs +2 -1
- package/crates/bsnext_fs/src/filter.rs +14 -5
- package/crates/bsnext_fs/src/test/mod.rs +1 -1
- package/crates/bsnext_fs/src/watch_path_handler.rs +1 -1
- package/crates/bsnext_fs/src/watcher.rs +2 -1
- package/crates/bsnext_input/Cargo.toml +3 -0
- package/crates/bsnext_input/src/input_test/mod.rs +34 -0
- package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap +29 -0
- package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap +25 -0
- package/crates/bsnext_input/src/route.rs +4 -0
- package/crates/bsnext_output/src/pretty.rs +1 -1
- package/crates/bsnext_system/Cargo.toml +1 -1
- package/crates/bsnext_system/src/args.rs +4 -0
- package/crates/bsnext_system/src/monitor_any_watchables.rs +20 -14
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +2 -1
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +2 -1
- package/crates/bsnext_tracing/src/lib.rs +42 -4
- package/examples/kitchen-sink/input.yml +1 -1
- package/package.json +17 -17
- package/crates/bsnext_client/style.css +0 -3
- package/crates/bsnext_core/src/not_found/not_found.html +0 -20
- package/crates/bsnext_core/src/not_found/route_list.rs +0 -49
- /package/crates/bsnext_client/{ts → inject/src}/console.ts +0 -0
|
@@ -6,8 +6,67 @@ use std::path::Path;
|
|
|
6
6
|
|
|
7
7
|
use crate::server::handler_change::{Change, ChangeKind};
|
|
8
8
|
use bsnext_fs::Debounce;
|
|
9
|
+
use bsnext_input::route::{DirRoute, ProxyRoute, Route, RouteKind};
|
|
9
10
|
use typeshare::typeshare;
|
|
10
11
|
|
|
12
|
+
#[typeshare]
|
|
13
|
+
#[derive(Debug, serde::Serialize)]
|
|
14
|
+
pub struct ServerDesc {
|
|
15
|
+
pub routes: Vec<RouteDTO>,
|
|
16
|
+
pub id: String,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#[typeshare]
|
|
20
|
+
#[derive(Debug, serde::Serialize)]
|
|
21
|
+
pub struct RouteDTO {
|
|
22
|
+
pub path: String,
|
|
23
|
+
pub kind: RouteKindDTO,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl From<Route> for RouteDTO {
|
|
27
|
+
fn from(value: Route) -> Self {
|
|
28
|
+
Self {
|
|
29
|
+
path: value.path.to_owned(),
|
|
30
|
+
kind: RouteKindDTO::from(value.kind),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
impl From<&Route> for RouteDTO {
|
|
35
|
+
fn from(value: &Route) -> Self {
|
|
36
|
+
Self {
|
|
37
|
+
path: value.path.to_owned(),
|
|
38
|
+
kind: RouteKindDTO::from(value.kind.clone()),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#[typeshare]
|
|
44
|
+
#[derive(Debug, serde::Serialize)]
|
|
45
|
+
#[serde(tag = "kind", content = "payload")]
|
|
46
|
+
pub enum RouteKindDTO {
|
|
47
|
+
Html { html: String },
|
|
48
|
+
Json { json_str: String },
|
|
49
|
+
Raw { raw: String },
|
|
50
|
+
Sse { sse: String },
|
|
51
|
+
Proxy { proxy: String },
|
|
52
|
+
Dir { dir: String },
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
impl From<RouteKind> for RouteKindDTO {
|
|
56
|
+
fn from(value: RouteKind) -> Self {
|
|
57
|
+
match value {
|
|
58
|
+
RouteKind::Html { html } => RouteKindDTO::Html { html },
|
|
59
|
+
RouteKind::Json { json } => RouteKindDTO::Json {
|
|
60
|
+
json_str: serde_json::to_string(&json).expect("unreachable"),
|
|
61
|
+
},
|
|
62
|
+
RouteKind::Raw { raw } => RouteKindDTO::Raw { raw },
|
|
63
|
+
RouteKind::Sse { sse } => RouteKindDTO::Sse { sse },
|
|
64
|
+
RouteKind::Proxy(ProxyRoute { proxy }) => RouteKindDTO::Proxy { proxy },
|
|
65
|
+
RouteKind::Dir(DirRoute { dir }) => RouteKindDTO::Dir { dir },
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
11
70
|
#[typeshare]
|
|
12
71
|
#[derive(Debug, serde::Serialize)]
|
|
13
72
|
pub struct ServersStarted {
|
|
@@ -157,12 +216,13 @@ pub struct ServerChangeSet {
|
|
|
157
216
|
#[typeshare::typeshare]
|
|
158
217
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, MessageResponse)]
|
|
159
218
|
pub struct GetServersMessageResponse {
|
|
160
|
-
pub servers: Vec<
|
|
219
|
+
pub servers: Vec<ServerDTO>,
|
|
161
220
|
}
|
|
162
221
|
|
|
163
222
|
#[typeshare::typeshare]
|
|
164
223
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
165
|
-
pub struct
|
|
224
|
+
pub struct ServerDTO {
|
|
225
|
+
pub id: String,
|
|
166
226
|
pub identity: IdentityDTO,
|
|
167
227
|
pub socket_addr: String,
|
|
168
228
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
use axum::extract::
|
|
1
|
+
use axum::extract::Request;
|
|
2
2
|
use axum::middleware::Next;
|
|
3
|
-
use std::sync::Arc;
|
|
4
3
|
|
|
5
4
|
use axum::response::{IntoResponse, Response};
|
|
6
5
|
|
|
@@ -9,15 +8,11 @@ use http::{HeaderValue, StatusCode};
|
|
|
9
8
|
use mime_guess::mime;
|
|
10
9
|
|
|
11
10
|
use crate::handlers::proxy::AnyAppError;
|
|
12
|
-
|
|
13
|
-
use
|
|
11
|
+
|
|
12
|
+
use bsnext_client::html_with_base;
|
|
14
13
|
use tracing::{span, Level};
|
|
15
14
|
|
|
16
|
-
pub async fn not_found_loader(
|
|
17
|
-
State(state): State<Arc<ServerState>>,
|
|
18
|
-
req: Request,
|
|
19
|
-
next: Next,
|
|
20
|
-
) -> Result<Response, AnyAppError> {
|
|
15
|
+
pub async fn not_found_loader(req: Request, next: Next) -> Result<Response, AnyAppError> {
|
|
21
16
|
let span = span!(parent: None, Level::INFO, "not_found", path = req.uri().path());
|
|
22
17
|
let _guard = span.enter();
|
|
23
18
|
|
|
@@ -26,8 +21,7 @@ pub async fn not_found_loader(
|
|
|
26
21
|
return Ok(r);
|
|
27
22
|
};
|
|
28
23
|
|
|
29
|
-
let
|
|
30
|
-
let markup = create_route_list_html(&routes);
|
|
24
|
+
let markup = html_with_base("/__bs_assets/ui/");
|
|
31
25
|
|
|
32
26
|
Ok((
|
|
33
27
|
StatusCode::NOT_FOUND,
|
|
@@ -11,17 +11,18 @@ use crate::meta::MetaData;
|
|
|
11
11
|
use crate::server::state::ServerState;
|
|
12
12
|
use axum::body::Body;
|
|
13
13
|
use axum::response::sse::Event;
|
|
14
|
-
use bsnext_input::route::{
|
|
14
|
+
use bsnext_input::route::{DirRoute, ProxyRoute, RouteKind};
|
|
15
15
|
use bsnext_resp::response_modifications_layer;
|
|
16
16
|
use bytes::Bytes;
|
|
17
17
|
use http::StatusCode;
|
|
18
18
|
use http_body_util::BodyExt;
|
|
19
19
|
use std::sync::Arc;
|
|
20
20
|
use std::time::Duration;
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
use tokio_stream::StreamExt;
|
|
23
23
|
use tower::ServiceExt;
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
use crate::common_layers::add_route_layers;
|
|
25
26
|
use tracing::{span, Level};
|
|
26
27
|
|
|
27
28
|
// use futures_util::stream::{self, Stream};
|
|
@@ -145,37 +146,6 @@ async fn tag_raw(req: Request, next: Next) -> Result<impl IntoResponse, (StatusC
|
|
|
145
146
|
Ok(Response::from_parts(parts, body))
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
pub fn add_route_layers(app: Router, route: &Route) -> Router {
|
|
149
|
-
let mut app = app;
|
|
150
|
-
|
|
151
|
-
if route
|
|
152
|
-
.cors_opts
|
|
153
|
-
.as_ref()
|
|
154
|
-
.is_some_and(|v| *v == CorsOpts::Cors(true))
|
|
155
|
-
{
|
|
156
|
-
tracing::trace!(to = route.path, "adding permissive cors");
|
|
157
|
-
app = app.layer(CorsLayer::permissive());
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if let Some(DelayOpts::Delay(DelayKind::Ms(ms))) = route.delay_opts.as_ref() {
|
|
161
|
-
tracing::trace!(to = route.path, ?ms, "adding delay");
|
|
162
|
-
let ms = *ms;
|
|
163
|
-
app = app.layer(middleware::from_fn(
|
|
164
|
-
move |req: Request, next: Next| async move {
|
|
165
|
-
let res = next.run(req).await;
|
|
166
|
-
sleep(Duration::from_millis(ms)).await;
|
|
167
|
-
Ok::<_, Infallible>(res)
|
|
168
|
-
},
|
|
169
|
-
));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// if route.opts.as_ref().is_some_and(|v| v.buff) {
|
|
173
|
-
// app = app.layer(middleware::from_fn(print_request_response));
|
|
174
|
-
// }
|
|
175
|
-
|
|
176
|
-
app
|
|
177
|
-
}
|
|
178
|
-
|
|
179
149
|
#[allow(dead_code)]
|
|
180
150
|
async fn print_request_response(
|
|
181
151
|
req: Request,
|
|
@@ -2,7 +2,8 @@ use crate::server::actor::ServerActor;
|
|
|
2
2
|
use crate::server::error::ServerError;
|
|
3
3
|
use crate::server::router::make_router;
|
|
4
4
|
use crate::server::state::ServerState;
|
|
5
|
-
use
|
|
5
|
+
use crate::servers_supervisor::actor::ServersSupervisor;
|
|
6
|
+
use actix::{Addr, AsyncContext};
|
|
6
7
|
use actix_rt::Arbiter;
|
|
7
8
|
use bsnext_input::server_config::Identity;
|
|
8
9
|
use std::future::Future;
|
|
@@ -14,13 +15,15 @@ use tokio::sync::{oneshot, RwLock};
|
|
|
14
15
|
|
|
15
16
|
#[derive(actix::Message)]
|
|
16
17
|
#[rtype(result = "Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>")]
|
|
17
|
-
pub struct Listen
|
|
18
|
+
pub struct Listen {
|
|
19
|
+
pub(crate) parent: Addr<ServersSupervisor>,
|
|
20
|
+
}
|
|
18
21
|
|
|
19
22
|
impl actix::Handler<Listen> for ServerActor {
|
|
20
23
|
type Result =
|
|
21
24
|
Pin<Box<dyn Future<Output = Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>>>>;
|
|
22
25
|
|
|
23
|
-
fn handle(&mut self,
|
|
26
|
+
fn handle(&mut self, msg: Listen, ctx: &mut Self::Context) -> Self::Result {
|
|
24
27
|
let identity = self.config.identity.clone();
|
|
25
28
|
tracing::trace!("actor started for {:?}", identity);
|
|
26
29
|
let (send_complete, handle, client_sender) = self.install_signals();
|
|
@@ -29,7 +32,10 @@ impl actix::Handler<Listen> for ServerActor {
|
|
|
29
32
|
let h2 = handle.clone();
|
|
30
33
|
|
|
31
34
|
let app_state = Arc::new(ServerState {
|
|
35
|
+
// parent: ,
|
|
32
36
|
routes: Arc::new(RwLock::new(self.config.routes.clone())),
|
|
37
|
+
id: self.config.identity.as_id(),
|
|
38
|
+
parent: Some(msg.parent.clone()),
|
|
33
39
|
client_sender: Arc::new(client_sender),
|
|
34
40
|
});
|
|
35
41
|
|
|
@@ -103,7 +109,7 @@ impl actix::Handler<Listen> for ServerActor {
|
|
|
103
109
|
listening = h2.listening() => {
|
|
104
110
|
match listening {
|
|
105
111
|
Some(socket_addr) => {
|
|
106
|
-
tracing::debug!("
|
|
112
|
+
tracing::debug!("{} listening...", socket_addr);
|
|
107
113
|
Ok((socket_addr, self_addr.clone()))
|
|
108
114
|
}
|
|
109
115
|
None => {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
use crate::server::state::ServerState;
|
|
2
|
+
use axum::response::IntoResponse;
|
|
3
|
+
use axum::routing::get;
|
|
4
|
+
use axum::Router;
|
|
5
|
+
use bsnext_client::{UI_CSS, UI_JS};
|
|
6
|
+
use http::header::CONTENT_TYPE;
|
|
7
|
+
use http::HeaderValue;
|
|
8
|
+
use mime_guess::mime;
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
|
|
11
|
+
pub fn pub_ui_assets(_: Arc<ServerState>) -> Router<Arc<ServerState>> {
|
|
12
|
+
Router::new()
|
|
13
|
+
.route(
|
|
14
|
+
"/dist/index.js",
|
|
15
|
+
get(|| async {
|
|
16
|
+
(
|
|
17
|
+
[(
|
|
18
|
+
CONTENT_TYPE,
|
|
19
|
+
HeaderValue::from_static(mime::APPLICATION_JAVASCRIPT.as_ref()),
|
|
20
|
+
)],
|
|
21
|
+
UI_JS,
|
|
22
|
+
)
|
|
23
|
+
.into_response()
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
.route(
|
|
27
|
+
"/dist/index.css",
|
|
28
|
+
get(|| async {
|
|
29
|
+
(
|
|
30
|
+
[(
|
|
31
|
+
CONTENT_TYPE,
|
|
32
|
+
HeaderValue::from_static(mime::TEXT_CSS.as_ref()),
|
|
33
|
+
)],
|
|
34
|
+
UI_CSS,
|
|
35
|
+
)
|
|
36
|
+
.into_response()
|
|
37
|
+
}),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -21,15 +21,20 @@ use tower::ServiceBuilder;
|
|
|
21
21
|
use tower_http::catch_panic::CatchPanicLayer;
|
|
22
22
|
|
|
23
23
|
use crate::dir_loader::serve_dir_loader;
|
|
24
|
+
use crate::dto::{RouteDTO, ServerDesc};
|
|
24
25
|
use crate::meta::MetaData;
|
|
25
26
|
use crate::not_found::not_found_service::not_found_loader;
|
|
26
|
-
use crate::not_found::route_list::create_route_list_html;
|
|
27
27
|
use crate::raw_loader::raw_loader;
|
|
28
|
+
use crate::server::router::assets::pub_ui_assets;
|
|
29
|
+
use crate::server::router::pub_api::pub_api;
|
|
28
30
|
use crate::server::state::ServerState;
|
|
29
31
|
use crate::ws::ws_handler;
|
|
32
|
+
use bsnext_client::html_with_base;
|
|
30
33
|
use tower_http::timeout::TimeoutLayer;
|
|
31
34
|
use tower_http::trace::TraceLayer;
|
|
32
35
|
|
|
36
|
+
mod assets;
|
|
37
|
+
mod pub_api;
|
|
33
38
|
mod tests;
|
|
34
39
|
|
|
35
40
|
pub fn make_router(state: &Arc<ServerState>) -> Router {
|
|
@@ -49,7 +54,11 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
|
|
|
49
54
|
async fn handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
|
|
50
55
|
// let v = app.lock().await;
|
|
51
56
|
let routes = app.routes.read().await;
|
|
52
|
-
let
|
|
57
|
+
let _dto = ServerDesc {
|
|
58
|
+
routes: routes.iter().map(RouteDTO::from).collect(),
|
|
59
|
+
id: app.id.to_string(),
|
|
60
|
+
};
|
|
61
|
+
let markup = html_with_base("/__bs_assets/ui/");
|
|
53
62
|
(
|
|
54
63
|
[(
|
|
55
64
|
CONTENT_TYPE,
|
|
@@ -60,7 +69,7 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
|
|
|
60
69
|
.into_response()
|
|
61
70
|
}
|
|
62
71
|
async fn js_handler(_uri: Uri) -> impl IntoResponse {
|
|
63
|
-
let markup = include_str!("../../../../bsnext_client/dist/index.js");
|
|
72
|
+
let markup = include_str!("../../../../bsnext_client/inject/dist/index.js");
|
|
64
73
|
(
|
|
65
74
|
[(
|
|
66
75
|
CONTENT_TYPE,
|
|
@@ -71,8 +80,10 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
|
|
|
71
80
|
.into_response()
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
route("/
|
|
83
|
+
route("/__bslive", get(handler))
|
|
75
84
|
.route("/__bs_js", get(js_handler))
|
|
85
|
+
.nest("/__bs_api", pub_api(state.clone()))
|
|
86
|
+
.nest("/__bs_assets/ui", pub_ui_assets(state.clone()))
|
|
76
87
|
.route("/__bs_ws", get(ws_handler))
|
|
77
88
|
.with_state(state.clone())
|
|
78
89
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
use crate::dto::{RouteDTO, ServerDesc};
|
|
2
|
+
use crate::server::state::ServerState;
|
|
3
|
+
use crate::servers_supervisor::get_servers_handler::GetServersMessage;
|
|
4
|
+
use axum::extract::State;
|
|
5
|
+
use axum::response::IntoResponse;
|
|
6
|
+
use axum::routing::get;
|
|
7
|
+
use axum::{Json, Router};
|
|
8
|
+
use http::{StatusCode, Uri};
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
|
|
11
|
+
async fn all_servers_handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
|
|
12
|
+
match &app.parent {
|
|
13
|
+
None => unreachable!("cannot get here"),
|
|
14
|
+
Some(parent) => match parent.send(GetServersMessage).await {
|
|
15
|
+
Ok(servers) => Json(servers).into_response(),
|
|
16
|
+
Err(err) => (
|
|
17
|
+
StatusCode::INTERNAL_SERVER_ERROR,
|
|
18
|
+
format!("Could not fetch servers: {}", err),
|
|
19
|
+
)
|
|
20
|
+
.into_response(),
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async fn get_current_server(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
|
|
26
|
+
let routes = app.routes.read().await;
|
|
27
|
+
let dto = ServerDesc {
|
|
28
|
+
routes: routes.iter().map(RouteDTO::from).collect(),
|
|
29
|
+
id: app.id.to_string(),
|
|
30
|
+
};
|
|
31
|
+
Json(dto)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn pub_api(state: Arc<ServerState>) -> Router<Arc<ServerState>> {
|
|
35
|
+
Router::new()
|
|
36
|
+
.route("/servers", get(all_servers_handler))
|
|
37
|
+
.route("/me", get(get_current_server))
|
|
38
|
+
.with_state(state.clone())
|
|
39
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#[cfg(test)]
|
|
2
2
|
mod test {
|
|
3
|
-
|
|
4
3
|
use crate::dto::ClientEvent;
|
|
5
4
|
use crate::server::router::make_router;
|
|
6
5
|
use crate::server::state::ServerState;
|
|
@@ -10,6 +9,7 @@ mod test {
|
|
|
10
9
|
use bsnext_input::route::{CorsOpts, Route, RouteKind};
|
|
11
10
|
use bsnext_input::server_config::{Identity, ServerConfig};
|
|
12
11
|
use http::HeaderValue;
|
|
12
|
+
use std::collections::BTreeMap;
|
|
13
13
|
use std::sync::Arc;
|
|
14
14
|
use tokio::sync::RwLock;
|
|
15
15
|
use tower::ServiceExt;
|
|
@@ -19,6 +19,8 @@ mod test {
|
|
|
19
19
|
let (sender, _) = tokio::sync::broadcast::channel::<ClientEvent>(10);
|
|
20
20
|
ServerState {
|
|
21
21
|
routes: Arc::new(RwLock::new(val.routes.clone())),
|
|
22
|
+
id: val.identity.as_id(),
|
|
23
|
+
parent: None,
|
|
22
24
|
client_sender: Arc::new(sender),
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -35,6 +37,8 @@ mod test {
|
|
|
35
37
|
|
|
36
38
|
#[tokio::test]
|
|
37
39
|
async fn test_handlers() -> Result<(), anyhow::Error> {
|
|
40
|
+
let headers: BTreeMap<String, String> =
|
|
41
|
+
[("a".to_string(), "b".to_string())].into_iter().collect();
|
|
38
42
|
let state: ServerState = ServerConfig {
|
|
39
43
|
identity: Identity::Address {
|
|
40
44
|
bind_address: "127.0.0.1:3000".to_string(),
|
|
@@ -42,6 +46,7 @@ mod test {
|
|
|
42
46
|
routes: vec![Route {
|
|
43
47
|
path: "/hello".to_string(),
|
|
44
48
|
kind: RouteKind::html("🐥"),
|
|
49
|
+
headers: Some(headers),
|
|
45
50
|
..Default::default()
|
|
46
51
|
}],
|
|
47
52
|
..Default::default()
|
|
@@ -52,7 +57,7 @@ mod test {
|
|
|
52
57
|
let req = Request::get("/hello").body(Body::empty()).unwrap();
|
|
53
58
|
let res = app.oneshot(req).await.unwrap();
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
insta::assert_debug_snapshot!(res.headers());
|
|
56
61
|
assert_eq!(
|
|
57
62
|
res.headers().get("content-type").unwrap(),
|
|
58
63
|
"text/html; charset=utf-8"
|
|
@@ -164,7 +169,7 @@ mod test {
|
|
|
164
169
|
|
|
165
170
|
let body = to_resp_body(res).await;
|
|
166
171
|
|
|
167
|
-
assert!(body.contains("<title>
|
|
172
|
+
assert!(body.contains("<title>Browsersync LIVE</title>"));
|
|
168
173
|
assert_eq!(status, 404);
|
|
169
174
|
Ok(())
|
|
170
175
|
}
|
|
@@ -185,7 +190,7 @@ mod test {
|
|
|
185
190
|
.into();
|
|
186
191
|
|
|
187
192
|
let app = make_router(&Arc::new(state));
|
|
188
|
-
let req = Request::get("/
|
|
193
|
+
let req = Request::get("/__bslive").body(Body::empty()).unwrap();
|
|
189
194
|
let res = app.oneshot(req).await.unwrap();
|
|
190
195
|
let status = res.status().as_u16();
|
|
191
196
|
|
|
@@ -196,8 +201,8 @@ mod test {
|
|
|
196
201
|
|
|
197
202
|
let body = to_resp_body(res).await;
|
|
198
203
|
|
|
199
|
-
assert!(body.contains("<title>
|
|
200
|
-
assert!(body.contains("<
|
|
204
|
+
assert!(body.contains("<title>Browsersync LIVE</title>"));
|
|
205
|
+
assert!(body.contains("<base href=\"/__bs_assets/ui/\" />"));
|
|
201
206
|
assert_eq!(status, 200);
|
|
202
207
|
Ok(())
|
|
203
208
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
use crate::dto::ClientEvent;
|
|
2
|
+
use crate::servers_supervisor::actor::ServersSupervisor;
|
|
3
|
+
use actix::Addr;
|
|
2
4
|
use bsnext_input::route::Route;
|
|
3
5
|
use std::fmt::Formatter;
|
|
4
6
|
use std::sync::Arc;
|
|
@@ -7,6 +9,8 @@ use tokio::sync::{broadcast, RwLock};
|
|
|
7
9
|
#[derive(Clone)]
|
|
8
10
|
pub struct ServerState {
|
|
9
11
|
pub routes: Arc<RwLock<Vec<Route>>>,
|
|
12
|
+
pub id: u64,
|
|
13
|
+
pub parent: Option<Addr<ServersSupervisor>>,
|
|
10
14
|
pub client_sender: Arc<broadcast::Sender<ClientEvent>>,
|
|
11
15
|
}
|
|
12
16
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::dto::{GetServersMessageResponse,
|
|
1
|
+
use crate::dto::{GetServersMessageResponse, ServerDTO};
|
|
2
2
|
use crate::servers_supervisor::actor::ServersSupervisor;
|
|
3
3
|
|
|
4
4
|
#[derive(actix::Message)]
|
|
@@ -13,7 +13,8 @@ impl actix::Handler<GetServersMessage> for ServersSupervisor {
|
|
|
13
13
|
servers: self
|
|
14
14
|
.handlers
|
|
15
15
|
.iter()
|
|
16
|
-
.map(|(identity, child_handler)|
|
|
16
|
+
.map(|(identity, child_handler)| ServerDTO {
|
|
17
|
+
id: identity.as_id().to_string(),
|
|
17
18
|
identity: identity.into(),
|
|
18
19
|
socket_addr: child_handler.socket_addr.to_string(),
|
|
19
20
|
})
|
|
@@ -31,7 +31,11 @@ impl actix::Handler<StartMessage> for ServersSupervisor {
|
|
|
31
31
|
let server = ServerActor::new_from_config(server_config.clone());
|
|
32
32
|
let actor_addr = server.start();
|
|
33
33
|
let c = server_config.clone();
|
|
34
|
-
actor_addr
|
|
34
|
+
actor_addr
|
|
35
|
+
.send(Listen {
|
|
36
|
+
parent: self_addr.clone(),
|
|
37
|
+
})
|
|
38
|
+
.map(|r| (r, c))
|
|
35
39
|
})
|
|
36
40
|
.collect::<Vec<_>>();
|
|
37
41
|
|
|
@@ -14,7 +14,7 @@ use tokio::sync::{broadcast, mpsc};
|
|
|
14
14
|
use tokio_stream::wrappers::ReceiverStream;
|
|
15
15
|
|
|
16
16
|
pub struct FsWatcher {
|
|
17
|
-
pub watcher: Option<notify::
|
|
17
|
+
pub watcher: Option<notify::RecommendedWatcher>,
|
|
18
18
|
raw_fs_stream: Arc<broadcast::Sender<InnerChangeEvent>>,
|
|
19
19
|
pub receivers: Vec<Recipient<FsEvent>>,
|
|
20
20
|
pub ctx: FsEventContext,
|
|
@@ -50,6 +50,7 @@ impl FsWatcher {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
pub fn with_filter(&mut self, f: Filter) {
|
|
53
|
+
tracing::debug!("adding filter {:?}", f);
|
|
53
54
|
self.filters.push(f)
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -12,13 +12,22 @@ impl PathFilter for Filter {
|
|
|
12
12
|
fn filter(&self, pd: &PathDescription) -> bool {
|
|
13
13
|
match self {
|
|
14
14
|
Filter::None => false,
|
|
15
|
-
Filter::Extension { ext } =>
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
15
|
+
Filter::Extension { ext } => {
|
|
16
|
+
tracing::trace!("testing extension `{:?}` against `{}`", pd, ext);
|
|
17
|
+
pd.absolute
|
|
18
|
+
.extension()
|
|
19
|
+
.is_some_and(|x| x.to_string_lossy() == *ext)
|
|
20
|
+
}
|
|
19
21
|
Filter::Glob { glob } => {
|
|
20
22
|
let target = pd.relative.unwrap_or(pd.absolute);
|
|
21
|
-
let
|
|
23
|
+
let compare = target.to_string_lossy().to_string();
|
|
24
|
+
let did_match = glob_match(glob, &compare);
|
|
25
|
+
tracing::trace!(
|
|
26
|
+
"testing glob `{}` against `{}`: {}",
|
|
27
|
+
glob,
|
|
28
|
+
compare.as_str(),
|
|
29
|
+
did_match
|
|
30
|
+
);
|
|
22
31
|
did_match
|
|
23
32
|
}
|
|
24
33
|
}
|
|
@@ -131,7 +131,7 @@ async fn test_single_file_impl() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
131
131
|
let tc = TestCase::new(Debounce::trailing_ms(10), None);
|
|
132
132
|
tc.watch().await;
|
|
133
133
|
tc.write_file("test_file.txt").await;
|
|
134
|
-
let events = tc.get_events_after(Duration::from_millis(
|
|
134
|
+
let events = tc.get_events_after(Duration::from_millis(500)).await;
|
|
135
135
|
assert_eq!(events.len(), 2);
|
|
136
136
|
assert_eq!(
|
|
137
137
|
matches!(events.get(0).unwrap().kind, FsEventKind::PathAdded(..)),
|
|
@@ -29,7 +29,7 @@ impl Handler<RequestWatchPath> for FsWatcher {
|
|
|
29
29
|
|
|
30
30
|
tracing::debug!(path = ?msg.path, "👀 watching! {} receivers", self.receivers.len());
|
|
31
31
|
tracing::debug!(?self.cwd);
|
|
32
|
-
tracing::debug!(
|
|
32
|
+
tracing::debug!("{} receivers", self.receivers.len());
|
|
33
33
|
|
|
34
34
|
let matched = msg.path == self.cwd;
|
|
35
35
|
|
|
@@ -13,7 +13,7 @@ use tokio::sync::broadcast;
|
|
|
13
13
|
pub fn create_watcher(
|
|
14
14
|
sender: Arc<broadcast::Sender<InnerChangeEvent>>,
|
|
15
15
|
cwd: &Path,
|
|
16
|
-
) -> notify::Result<notify::
|
|
16
|
+
) -> notify::Result<notify::RecommendedWatcher> {
|
|
17
17
|
let cwd_c = cwd.to_owned();
|
|
18
18
|
notify::recommended_watcher(move |res: Result<notify::Event, _>| {
|
|
19
19
|
let span = tracing::span!(tracing::Level::TRACE, "raw");
|
|
@@ -247,6 +247,7 @@ fn is_auto_excluded<P: AsRef<Path>>(cwd: &P, subject: &P) -> bool {
|
|
|
247
247
|
".vscode",
|
|
248
248
|
".idea",
|
|
249
249
|
".sass-cache",
|
|
250
|
+
"bslive.log",
|
|
250
251
|
]
|
|
251
252
|
.into_iter()
|
|
252
253
|
.map(OsStr::new)
|
|
@@ -62,6 +62,40 @@ fn test_deserialize_cors_false() {
|
|
|
62
62
|
assert_eq!(opts, CorsOpts::Cors(false));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
#[test]
|
|
66
|
+
fn test_deserialize_3_headers_control() {
|
|
67
|
+
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
|
68
|
+
struct Config {
|
|
69
|
+
pub items: Vec<Route>,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let input = r#"
|
|
73
|
+
items:
|
|
74
|
+
- path: /api
|
|
75
|
+
json: [1,2]
|
|
76
|
+
"#;
|
|
77
|
+
let c: Config = serde_yaml::from_str(input).unwrap();
|
|
78
|
+
insta::assert_debug_snapshot!(c)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[test]
|
|
82
|
+
fn test_deserialize_3_headers() {
|
|
83
|
+
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
|
84
|
+
struct Config {
|
|
85
|
+
pub items: Vec<Route>,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let input = r#"
|
|
89
|
+
items:
|
|
90
|
+
- path: /api
|
|
91
|
+
json: [1,2]
|
|
92
|
+
headers:
|
|
93
|
+
a: b
|
|
94
|
+
"#;
|
|
95
|
+
let c: Config = serde_yaml::from_str(input).unwrap();
|
|
96
|
+
insta::assert_debug_snapshot!(c)
|
|
97
|
+
}
|
|
98
|
+
|
|
65
99
|
#[test]
|
|
66
100
|
fn test_deserialize_3() {
|
|
67
101
|
let input = r#"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: crates/bsnext_input/src/input_test/mod.rs
|
|
3
|
+
expression: c
|
|
4
|
+
---
|
|
5
|
+
Config {
|
|
6
|
+
items: [
|
|
7
|
+
Route {
|
|
8
|
+
path: "/api",
|
|
9
|
+
cors_opts: None,
|
|
10
|
+
delay_opts: None,
|
|
11
|
+
watch_opts: Bool(
|
|
12
|
+
true,
|
|
13
|
+
),
|
|
14
|
+
kind: Json {
|
|
15
|
+
json: JsonWrapper(
|
|
16
|
+
Array [
|
|
17
|
+
Number(1),
|
|
18
|
+
Number(2),
|
|
19
|
+
],
|
|
20
|
+
),
|
|
21
|
+
},
|
|
22
|
+
headers: Some(
|
|
23
|
+
{
|
|
24
|
+
"a": "b",
|
|
25
|
+
},
|
|
26
|
+
),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
}
|