Compare commits
No commits in common. "d61e6da1d205eeee38124e9eb67a8e7d7fa73fe3" and "59b5c180a67efd3195677cb9811c587120f6bb01" have entirely different histories.
d61e6da1d2
...
59b5c180a6
@ -14,7 +14,6 @@ serde_json = "1.0.140"
|
|||||||
sqlx = { version = "0.8.5", features = ["macros", "runtime-tokio", "sqlite", "uuid"] }
|
sqlx = { version = "0.8.5", features = ["macros", "runtime-tokio", "sqlite", "uuid"] }
|
||||||
tokio = { version = "1.44.2", features = ["full"] }
|
tokio = { version = "1.44.2", features = ["full"] }
|
||||||
tower = "0.5.2"
|
tower = "0.5.2"
|
||||||
tower-http = { version = "0.6.4", features = ["fs"] }
|
|
||||||
tracing = { version = "0.1.41", features = ["async-await"] }
|
tracing = { version = "0.1.41", features = ["async-await"] }
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
uuid = { version = "1.16.0", features = ["v4"] }
|
uuid = { version = "1.16.0", features = ["v4"] }
|
||||||
|
1
assets/assets/htmx.min.js
vendored
1
assets/assets/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,33 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<script src="assets/htmx.min.js"></script>
|
|
||||||
<title>WebSocket Example</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>Hello World</h1>
|
|
||||||
<div hx-get="/scripts" hx-swap="outerHTML" hx-trigger="load"></div>
|
|
||||||
<!-- <input id="msg" type="text" placeholder="Enter message"> -->
|
|
||||||
<!-- <button onclick="sendMessage()">Send</button> -->
|
|
||||||
<!-- <div id="log"></div> -->
|
|
||||||
<!---->
|
|
||||||
<!-- <script> -->
|
|
||||||
<!-- const ws = new WebSocket("ws://localhost:3000/ws"); -->
|
|
||||||
<!---->
|
|
||||||
<!-- ws.onmessage = (event) => { -->
|
|
||||||
<!-- console.log(event); -->
|
|
||||||
<!-- const log = document.getElementById("log"); -->
|
|
||||||
<!-- log.innerHTML += `<p>${event.data}</p>`; -->
|
|
||||||
<!-- }; -->
|
|
||||||
<!---->
|
|
||||||
<!-- function sendMessage() { -->
|
|
||||||
<!-- const msg = document.getElementById("msg").value; -->
|
|
||||||
<!-- ws.send(msg); -->
|
|
||||||
<!-- } -->
|
|
||||||
<!-- </script> -->
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -11,7 +11,6 @@
|
|||||||
in {
|
in {
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
nodejs
|
|
||||||
powershell
|
powershell
|
||||||
];
|
];
|
||||||
|
|
||||||
|
25
index.html
Normal file
25
index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head><meta charset="UTF-8"><title>WebSocket Example</title></head>
|
||||||
|
<body>
|
||||||
|
<input id="msg" type="text" placeholder="Enter message">
|
||||||
|
<button onclick="sendMessage()">Send</button>
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ws = new WebSocket("ws://localhost:3000/ws");
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
console.log(event);
|
||||||
|
const log = document.getElementById("log");
|
||||||
|
log.innerHTML += `<p>${event.data}</p>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendMessage() {
|
||||||
|
const msg = document.getElementById("msg").value;
|
||||||
|
ws.send(msg);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
19
src/lib.rs
19
src/lib.rs
@ -1,21 +1,2 @@
|
|||||||
#![allow(clippy::unwrap_used)]
|
|
||||||
use router::new_router;
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
mod router;
|
|
||||||
mod script;
|
mod script;
|
||||||
|
|
||||||
pub fn run() {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
rt.block_on(async {
|
|
||||||
tracing_subscriber::fmt::init();
|
|
||||||
|
|
||||||
let app = new_router();
|
|
||||||
|
|
||||||
let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
|
||||||
info!("listening"=?listener.local_addr().unwrap());
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
188
src/main.rs
188
src/main.rs
@ -1,13 +1,16 @@
|
|||||||
#![allow(dead_code, clippy::unwrap_used, clippy::expect_used)]
|
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||||
use axum::{
|
use axum::{
|
||||||
// Router,
|
Router,
|
||||||
extract::ws::{Message, WebSocket, WebSocketUpgrade},
|
extract::ws::{Message, WebSocket, WebSocketUpgrade},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
// routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::{
|
||||||
use tokio::task;
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use tokio::{task, time};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
@ -17,71 +20,118 @@ use tokio::sync::mpsc; // Use std::process::Stdio
|
|||||||
|
|
||||||
type Tx = tokio::sync::mpsc::UnboundedSender<Message>;
|
type Tx = tokio::sync::mpsc::UnboundedSender<Message>;
|
||||||
|
|
||||||
fn main() {
|
#[tokio::main]
|
||||||
befehlswerk::run();
|
async fn main() {
|
||||||
|
tracing_subscriber::fmt().pretty().init();
|
||||||
|
let clients = Arc::new(Mutex::new(Vec::<Tx>::new()));
|
||||||
|
let clients_clone = Arc::clone(&clients);
|
||||||
|
|
||||||
|
let (tx, mut rx) = mpsc::channel(100);
|
||||||
|
|
||||||
|
task::spawn(async move {
|
||||||
|
let cmd = "$count = 0; while($count -lt 10000) {
|
||||||
|
#Write-Error 'test-error'
|
||||||
|
#write-warning 'test-warning'
|
||||||
|
Write-Output \"$(Get-Date -Format 'yyyy-MM-dd_HH:mm:ss'): Hello World #$($count) from PowerShell!\"
|
||||||
|
$count += 1
|
||||||
|
}";
|
||||||
|
spawn_and_capture(cmd, tx).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// In batches - but this does not preserve the order. Or does it? It's possible that stdout is
|
||||||
|
// faster than stderr.
|
||||||
|
task::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let mut closed = vec![];
|
||||||
|
|
||||||
|
let mut messages = vec![];
|
||||||
|
|
||||||
|
if rx.recv_many(&mut messages, 2000).await > 0 {
|
||||||
|
let msg = Message::Text(
|
||||||
|
messages
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| if m.1 { format!("[ERR]: {}", m.0) } else { m.0 })
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let mut lock = clients_clone.lock().unwrap();
|
||||||
|
for (i, tx) in lock.iter().enumerate() {
|
||||||
|
if tx.send(msg.clone()).is_err() {
|
||||||
|
closed.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in closed.iter().rev() {
|
||||||
|
lock.remove(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//for message in messages {
|
||||||
|
// let msg = Message::Text(if message.1 {
|
||||||
|
// format!("[ERR]: {}", message.0).into()
|
||||||
|
// } else {
|
||||||
|
// message.0.into()
|
||||||
|
// });
|
||||||
|
// {
|
||||||
|
// let mut lock = clients_clone.lock().unwrap();
|
||||||
|
// for (i, tx) in lock.iter().enumerate() {
|
||||||
|
// if tx.send(msg.clone()).is_err() {
|
||||||
|
// closed.push(i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for i in closed.iter().rev() {
|
||||||
|
// lock.remove(*i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
//time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//task::spawn(async move {
|
||||||
|
// loop {
|
||||||
|
// let mut closed = vec![];
|
||||||
|
//
|
||||||
|
// if let Some((line, is_err)) = rx.recv().await {
|
||||||
|
// let msg = Message::Text(if is_err {
|
||||||
|
// format!("[ERR]: {line}").into()
|
||||||
|
// } else {
|
||||||
|
// line.into()
|
||||||
|
// });
|
||||||
|
// {
|
||||||
|
// let mut lock = clients_clone.lock().unwrap();
|
||||||
|
// for (i, tx) in lock.iter().enumerate() {
|
||||||
|
// if tx.send(msg.clone()).is_err() {
|
||||||
|
// closed.push(i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for i in closed.iter().rev() {
|
||||||
|
// lock.remove(*i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //time::sleep(Duration::from_millis(200)).await;
|
||||||
|
// } else {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
|
||||||
|
let app = Router::new().route(
|
||||||
|
"/ws",
|
||||||
|
get({
|
||||||
|
let clients = Arc::clone(&clients);
|
||||||
|
move |ws| handle_socket(ws, Arc::clone(&clients))
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
// #[tokio::main]
|
|
||||||
// async fn main() {
|
|
||||||
// tracing_subscriber::fmt().pretty().init();
|
|
||||||
// let clients = Arc::new(Mutex::new(Vec::<Tx>::new()));
|
|
||||||
// let clients_clone = Arc::clone(&clients);
|
|
||||||
//
|
|
||||||
// let (tx, mut rx) = mpsc::channel(100);
|
|
||||||
//
|
|
||||||
// task::spawn(async move {
|
|
||||||
// let cmd = "$count = 0; while($count -lt 10000) {
|
|
||||||
// #Write-Error 'test-error'
|
|
||||||
// #write-warning 'test-warning'
|
|
||||||
// Write-Output \"$(Get-Date -Format 'yyyy-MM-dd_HH:mm:ss'): Hello World #$($count) from PowerShell!\"
|
|
||||||
// $count += 1
|
|
||||||
// }";
|
|
||||||
// spawn_and_capture(cmd, tx).await;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// // In batches - but this does not preserve the order. Or does it? It's possible that stdout is
|
|
||||||
// // faster than stderr.
|
|
||||||
// task::spawn(async move {
|
|
||||||
// loop {
|
|
||||||
// let mut closed = vec![];
|
|
||||||
//
|
|
||||||
// let mut messages = vec![];
|
|
||||||
//
|
|
||||||
// if rx.recv_many(&mut messages, 2000).await > 0 {
|
|
||||||
// let msg = Message::Text(
|
|
||||||
// messages
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|m| if m.1 { format!("[ERR]: {}", m.0) } else { m.0 })
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// .join("\n")
|
|
||||||
// .into(),
|
|
||||||
// );
|
|
||||||
// {
|
|
||||||
// let mut lock = clients_clone.lock().unwrap();
|
|
||||||
// for (i, tx) in lock.iter().enumerate() {
|
|
||||||
// if tx.send(msg.clone()).is_err() {
|
|
||||||
// closed.push(i);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for i in closed.iter().rev() {
|
|
||||||
// lock.remove(*i);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// let app = Router::new().route(
|
|
||||||
// "/ws",
|
|
||||||
// get({
|
|
||||||
// let clients = Arc::clone(&clients);
|
|
||||||
// move |ws| handle_socket(ws, Arc::clone(&clients))
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
|
||||||
// axum::serve(listener, app).await.unwrap();
|
|
||||||
// }
|
|
||||||
|
|
||||||
async fn handle_socket(ws: WebSocketUpgrade, clients: Arc<Mutex<Vec<Tx>>>) -> impl IntoResponse {
|
async fn handle_socket(ws: WebSocketUpgrade, clients: Arc<Mutex<Vec<Tx>>>) -> impl IntoResponse {
|
||||||
ws.on_upgrade(move |socket| handle_ws(socket, clients))
|
ws.on_upgrade(move |socket| handle_ws(socket, clients))
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
use axum::routing::{get, get_service};
|
|
||||||
use routes::scripts::get_scripts;
|
|
||||||
use tower_http::services::ServeDir;
|
|
||||||
mod routes;
|
|
||||||
|
|
||||||
pub fn new_router() -> axum::Router {
|
|
||||||
axum::Router::new()
|
|
||||||
.fallback_service(get_service(ServeDir::new("assets")))
|
|
||||||
// .nest_service("/", ServeDir::new("assets"))
|
|
||||||
.route("/scripts", get(get_scripts))
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
pub mod scripts;
|
|
@ -1,14 +0,0 @@
|
|||||||
use axum::response::{Html, IntoResponse};
|
|
||||||
|
|
||||||
pub async fn get_scripts() -> impl IntoResponse {
|
|
||||||
Html(
|
|
||||||
r##"
|
|
||||||
<section id="scripts">
|
|
||||||
<ul>
|
|
||||||
<li>Script1</li>
|
|
||||||
<li>Script2</li>
|
|
||||||
</ul>
|
|
||||||
<button hx-get="/scripts" hx-swap="outerHTML" hx-target="#scripts">Refresh</button>
|
|
||||||
</section>"##,
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user