Skip to content

Commit 429f3a0

Browse files
committed
chore(example): redis adapter
1 parent 38e1590 commit 429f3a0

File tree

7 files changed

+272
-2
lines changed

7 files changed

+272
-2
lines changed

examples/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ rmpv = { version = "1.3.0", features = ["with-serde"] }
1111
tower = { version = "0.5.0", default-features = false }
1212
tracing = "0.1.37"
1313
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
14-
axum = "0.7.5"
14+
axum = "0.7"
1515
hyper-util.version = "0.1.1"
1616
hyper = { version = "1.0.1", features = ["http1", "server"] }
1717
socketioxide = { path = "../crates/socketioxide" }
18-
socketioxide-redis = { path = "../crates/socketioxide-redis" }

examples/redis-whiteboard/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "redis-whiteboard"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
socketioxide = { workspace = true, features = ["tracing", "msgpack"] }
8+
socketioxide-redis = { path = "../../crates/socketioxide-redis" }
9+
axum.workspace = true
10+
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
11+
tower-http = { version = "0.5.0", features = ["cors", "fs"] }
12+
tower.workspace = true
13+
tracing-subscriber.workspace = true
14+
tracing.workspace = true
15+
serde.workspace = true
16+
rmpv.workspace = true
17+
redis = "0.27"

examples/redis-whiteboard/Readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Same example than whiteboard but with a redis adapter
2+
3+
You can spawn as much as server as you want with different ports (env PORT) and then join with clients
4+
connected on these different ports.
5+
6+
The parser is set to msgpack in the example, but you can use any socket.io parser you want.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Socket.IO whiteboard</title>
6+
<link rel="stylesheet" href="style.css" />
7+
</head>
8+
9+
<body>
10+
<canvas class="whiteboard"></canvas>
11+
12+
<div class="colors">
13+
<div class="color black"></div>
14+
<div class="color red"></div>
15+
<div class="color green"></div>
16+
<div class="color blue"></div>
17+
<div class="color yellow"></div>
18+
</div>
19+
20+
<script src="https://cdn.socket.io/4.7.5/socket.io.msgpack.min.js"></script>
21+
<script src="/main.js"></script>
22+
</body>
23+
</html>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"use strict";
2+
3+
(function () {
4+
const params = new URLSearchParams(window.location.search);
5+
var socket = io();
6+
var canvas = document.getElementsByClassName("whiteboard")[0];
7+
var colors = document.getElementsByClassName("color");
8+
var context = canvas.getContext("2d");
9+
10+
var current = {
11+
color: "black",
12+
};
13+
var drawing = false;
14+
15+
canvas.addEventListener("mousedown", onMouseDown, false);
16+
canvas.addEventListener("mouseup", onMouseUp, false);
17+
canvas.addEventListener("mouseout", onMouseUp, false);
18+
canvas.addEventListener("mousemove", throttle(onMouseMove, 10), false);
19+
20+
//Touch support for mobile devices
21+
canvas.addEventListener("touchstart", onMouseDown, false);
22+
canvas.addEventListener("touchend", onMouseUp, false);
23+
canvas.addEventListener("touchcancel", onMouseUp, false);
24+
canvas.addEventListener("touchmove", throttle(onMouseMove, 10), false);
25+
26+
for (var i = 0; i < colors.length; i++) {
27+
colors[i].addEventListener("click", onColorUpdate, false);
28+
}
29+
30+
socket.on("drawing", onDrawingEvent);
31+
32+
window.addEventListener("resize", onResize, false);
33+
onResize();
34+
35+
function drawLine(x0, y0, x1, y1, color, emit) {
36+
context.beginPath();
37+
context.moveTo(x0, y0);
38+
context.lineTo(x1, y1);
39+
context.strokeStyle = color;
40+
context.lineWidth = 2;
41+
context.stroke();
42+
context.closePath();
43+
44+
if (!emit) {
45+
return;
46+
}
47+
var w = canvas.width;
48+
var h = canvas.height;
49+
50+
socket.emit("drawing", {
51+
x0: x0 / w,
52+
y0: y0 / h,
53+
x1: x1 / w,
54+
y1: y1 / h,
55+
color: color,
56+
});
57+
}
58+
59+
function onMouseDown(e) {
60+
drawing = true;
61+
current.x = e.clientX || e.touches[0].clientX;
62+
current.y = e.clientY || e.touches[0].clientY;
63+
}
64+
65+
function onMouseUp(e) {
66+
if (!drawing) {
67+
return;
68+
}
69+
drawing = false;
70+
drawLine(
71+
current.x,
72+
current.y,
73+
e.clientX || e.touches[0].clientX,
74+
e.clientY || e.touches[0].clientY,
75+
current.color,
76+
true,
77+
);
78+
}
79+
80+
function onMouseMove(e) {
81+
if (!drawing) {
82+
return;
83+
}
84+
drawLine(
85+
current.x,
86+
current.y,
87+
e.clientX || e.touches[0].clientX,
88+
e.clientY || e.touches[0].clientY,
89+
current.color,
90+
true,
91+
);
92+
current.x = e.clientX || e.touches[0].clientX;
93+
current.y = e.clientY || e.touches[0].clientY;
94+
}
95+
96+
function onColorUpdate(e) {
97+
current.color = e.target.className.split(" ")[1];
98+
}
99+
100+
// limit the number of events per second
101+
function throttle(callback, delay) {
102+
var previousCall = new Date().getTime();
103+
return function () {
104+
var time = new Date().getTime();
105+
106+
if (time - previousCall >= delay) {
107+
previousCall = time;
108+
callback.apply(null, arguments);
109+
}
110+
};
111+
}
112+
113+
function onDrawingEvent(data) {
114+
var w = canvas.width;
115+
var h = canvas.height;
116+
drawLine(data.x0 * w, data.y0 * h, data.x1 * w, data.y1 * h, data.color);
117+
}
118+
119+
// make the canvas fill its parent
120+
function onResize() {
121+
canvas.width = window.innerWidth;
122+
canvas.height = window.innerHeight;
123+
}
124+
})();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
/**
3+
* Fix user-agent
4+
*/
5+
6+
* {
7+
box-sizing: border-box;
8+
}
9+
10+
html, body {
11+
height: 100%;
12+
margin: 0;
13+
padding: 0;
14+
}
15+
16+
/**
17+
* Canvas
18+
*/
19+
20+
.whiteboard {
21+
height: 100%;
22+
width: 100%;
23+
position: absolute;
24+
left: 0;
25+
right: 0;
26+
bottom: 0;
27+
top: 0;
28+
}
29+
30+
.colors {
31+
position: fixed;
32+
}
33+
34+
.color {
35+
display: inline-block;
36+
height: 48px;
37+
width: 48px;
38+
}
39+
40+
.color.black { background-color: black; }
41+
.color.red { background-color: red; }
42+
.color.green { background-color: green; }
43+
.color.blue { background-color: blue; }
44+
.color.yellow { background-color: yellow; }

examples/redis-whiteboard/src/main.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use std::str::FromStr;
2+
3+
use rmpv::Value;
4+
use socketioxide::{
5+
ParserConfig, SocketIo,
6+
extract::{Data, SocketRef},
7+
};
8+
use socketioxide_redis::{RedisAdapter, RedisAdapterCtr};
9+
use tower::ServiceBuilder;
10+
use tower_http::{cors::CorsLayer, services::ServeDir};
11+
use tracing::info;
12+
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
13+
14+
#[tokio::main]
15+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
16+
tracing_subscriber::registry()
17+
.with(fmt::layer())
18+
.with(EnvFilter::from_default_env())
19+
.init();
20+
21+
info!("connecting to redis");
22+
let client = redis::Client::open("redis://127.0.0.1:6379?protocol=resp3").unwrap();
23+
let adapter = RedisAdapterCtr::new(client).await.unwrap();
24+
info!("starting server");
25+
26+
let (layer, io) = SocketIo::builder()
27+
.with_parser(ParserConfig::msgpack())
28+
.with_adapter::<RedisAdapter<_>>(adapter)
29+
.build_layer();
30+
31+
io.ns("/", |s: SocketRef<RedisAdapter<_>>| {
32+
s.on(
33+
"drawing",
34+
|s: SocketRef<RedisAdapter<_>>, Data::<Value>(data)| async move {
35+
s.broadcast().emit("drawing", &data).await.unwrap();
36+
},
37+
);
38+
});
39+
40+
let app = axum::Router::new()
41+
.nest_service("/", ServeDir::new("dist"))
42+
.layer(
43+
ServiceBuilder::new()
44+
.layer(CorsLayer::permissive()) // Enable CORS policy
45+
.layer(layer),
46+
);
47+
48+
let port: u16 = std::env::var("PORT")
49+
.map(|s| u16::from_str(&s).unwrap())
50+
.unwrap_or(3000);
51+
let listener = tokio::net::TcpListener::bind(("0.0.0.0", port))
52+
.await
53+
.unwrap();
54+
axum::serve(listener, app).await.unwrap();
55+
56+
Ok(())
57+
}

0 commit comments

Comments
 (0)