Compare commits
3 commits
c6cd325748
...
82a8418b82
| Author | SHA1 | Date | |
|---|---|---|---|
| 82a8418b82 | |||
| dabe5ef0fe | |||
|
|
91ce009271 |
2 changed files with 92 additions and 27 deletions
62
flake.nix
62
flake.nix
|
|
@ -14,30 +14,48 @@
|
||||||
] (system: f (import inputs.nixpkgs { inherit system; }));
|
] (system: f (import inputs.nixpkgs { inherit system; }));
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = forAllSystems (pkgs: {
|
packages = forAllSystems (
|
||||||
gitlab-job-status = pkgs.writeShellApplication {
|
pkgs:
|
||||||
name = "gitlab-job-status";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.curl
|
|
||||||
pkgs.jq
|
|
||||||
pkgs.git
|
|
||||||
pkgs.ncurses
|
|
||||||
pkgs.coreutils
|
|
||||||
];
|
|
||||||
|
|
||||||
text = builtins.readFile ./gitlab-job-status;
|
let
|
||||||
};
|
lanshare = pkgs.rustPlatform.buildRustPackage (finalAttrs: {
|
||||||
|
pname = "lanshare";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = ./lanshare;
|
||||||
|
cargoHash = "sha256-rUTARzVTbi0/KNFUDRynOTioIZIhiZf4suqb9UqtvV4=";
|
||||||
|
|
||||||
migrate-workspace-deps = pkgs.writeShellApplication {
|
meta = {
|
||||||
name = "migrate-workspace-deps";
|
description = "A local screenshare application";
|
||||||
runtimeInputs = [
|
mainProgram = "lanshare";
|
||||||
pkgs.jq
|
};
|
||||||
pkgs.git
|
});
|
||||||
pkgs.yq
|
in
|
||||||
];
|
{
|
||||||
|
inherit lanshare;
|
||||||
|
gitlab-job-status = pkgs.writeShellApplication {
|
||||||
|
name = "gitlab-job-status";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.git
|
||||||
|
pkgs.ncurses
|
||||||
|
pkgs.coreutils
|
||||||
|
];
|
||||||
|
|
||||||
text = builtins.readFile ./migrate-workspace-deps;
|
text = builtins.readFile ./gitlab-job-status;
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
migrate-workspace-deps = pkgs.writeShellApplication {
|
||||||
|
name = "migrate-workspace-deps";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.git
|
||||||
|
pkgs.yq
|
||||||
|
];
|
||||||
|
|
||||||
|
text = builtins.readFile ./migrate-workspace-deps;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,15 @@ struct Args {
|
||||||
/// connect via ws:// instead.
|
/// connect via ws:// instead.
|
||||||
#[arg(long, default_value_t = 8081, value_name = "PORT")]
|
#[arg(long, default_value_t = 8081, value_name = "PORT")]
|
||||||
http_port: u16,
|
http_port: u16,
|
||||||
|
|
||||||
|
/// Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IPs
|
||||||
|
///
|
||||||
|
/// Enable this when running behind a reverse proxy (e.g. nginx, caddy,
|
||||||
|
/// traefik). The server will use the IP from proxy headers instead of
|
||||||
|
/// the direct connection address. Do NOT enable this without a trusted
|
||||||
|
/// proxy, as clients can spoof these headers.
|
||||||
|
#[arg(long)]
|
||||||
|
trust_proxy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages sent between peers via the signaling server
|
/// Messages sent between peers via the signaling server
|
||||||
|
|
@ -155,12 +164,14 @@ struct PeerState {
|
||||||
/// Application state shared across all connections
|
/// Application state shared across all connections
|
||||||
struct AppState {
|
struct AppState {
|
||||||
peers: DashMap<String, PeerState>,
|
peers: DashMap<String, PeerState>,
|
||||||
|
trust_proxy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
fn new() -> Self {
|
fn new(trust_proxy: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
peers: DashMap::new(),
|
peers: DashMap::new(),
|
||||||
|
trust_proxy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,7 +213,7 @@ async fn main() {
|
||||||
)
|
)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let state = Arc::new(AppState::new());
|
let state = Arc::new(AppState::new(args.trust_proxy));
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(serve_index))
|
.route("/", get(serve_index))
|
||||||
|
|
@ -244,6 +255,9 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if args.trust_proxy {
|
||||||
|
println!(" \x1b[1;36mProxy mode:\x1b[0m trusting X-Forwarded-For / X-Real-IP headers");
|
||||||
|
}
|
||||||
println!();
|
println!();
|
||||||
println!(" \x1b[90mOpen this URL in browsers on your local network.\x1b[0m");
|
println!(" \x1b[90mOpen this URL in browsers on your local network.\x1b[0m");
|
||||||
println!(" \x1b[90mPress Ctrl+C to stop the server.\x1b[0m");
|
println!(" \x1b[90mPress Ctrl+C to stop the server.\x1b[0m");
|
||||||
|
|
@ -290,14 +304,47 @@ async fn serve_index() -> impl IntoResponse {
|
||||||
async fn ws_handler(
|
async fn ws_handler(
|
||||||
ws: WebSocketUpgrade,
|
ws: WebSocketUpgrade,
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||||
|
headers: axum::http::HeaderMap,
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
ws.on_upgrade(move |socket| handle_socket(socket, state, addr))
|
let peer_ip = if state.trust_proxy {
|
||||||
|
extract_proxy_ip(&headers).unwrap_or_else(|| addr.ip().to_string())
|
||||||
|
} else {
|
||||||
|
addr.ip().to_string()
|
||||||
|
};
|
||||||
|
ws.on_upgrade(move |socket| handle_socket(socket, state, peer_ip))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_socket(socket: WebSocket, state: Arc<AppState>, addr: SocketAddr) {
|
/// Extract the client IP from proxy headers.
|
||||||
|
/// Checks X-Forwarded-For first (uses the leftmost/first IP), then X-Real-IP.
|
||||||
|
fn extract_proxy_ip(headers: &axum::http::HeaderMap) -> Option<String> {
|
||||||
|
// X-Forwarded-For: client, proxy1, proxy2
|
||||||
|
if let Some(forwarded_for) = headers.get("x-forwarded-for") {
|
||||||
|
if let Ok(value) = forwarded_for.to_str() {
|
||||||
|
if let Some(first_ip) = value.split(',').next() {
|
||||||
|
let trimmed = first_ip.trim();
|
||||||
|
if !trimmed.is_empty() {
|
||||||
|
return Some(trimmed.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// X-Real-IP: client
|
||||||
|
if let Some(real_ip) = headers.get("x-real-ip") {
|
||||||
|
if let Ok(value) = real_ip.to_str() {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
if !trimmed.is_empty() {
|
||||||
|
return Some(trimmed.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_socket(socket: WebSocket, state: Arc<AppState>, peer_ip: String) {
|
||||||
let peer_id = Uuid::new_v4().to_string();
|
let peer_id = Uuid::new_v4().to_string();
|
||||||
let peer_ip = addr.ip().to_string();
|
|
||||||
let (tx, _) = broadcast::channel::<SignalMessage>(64);
|
let (tx, _) = broadcast::channel::<SignalMessage>(64);
|
||||||
|
|
||||||
// Add peer to state with default name
|
// Add peer to state with default name
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue