Skip to content

Commit e4e96d2

Browse files
authored
Merge pull request #22 from ks6088ts-labs/feature/issue-20_websockets
add websockets template
2 parents 1dc0fde + 47b5b0f commit e4e96d2

File tree

6 files changed

+97
-1
lines changed

6 files changed

+97
-1
lines changed

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ uv run python scripts/speeches.py delete-transcription "$JOB_ID" --force
122122

123123
- [FastAPI](https://fastapi.tiangolo.com/)
124124
- [Settings and Environment Variables](https://fastapi.tiangolo.com/advanced/settings/)
125+
- [WebSockets](https://fastapi.tiangolo.com/advanced/websockets/)
125126

126127
## OpenTelemetry
127128

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies = [
1616
"opentelemetry-instrumentation-fastapi>=0.52b1",
1717
"pydantic-settings>=2.10.1",
1818
"typer>=0.16.0",
19+
"websockets>=15.0.1",
1920
]
2021

2122
[project.optional-dependencies]

template_fastapi/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
1212
from opentelemetry.trace import Span
1313

14-
from template_fastapi.routers import demos, files, foodies, games, items, speeches
14+
from template_fastapi.routers import chats, demos, files, foodies, games, items, speeches
1515

1616
app = FastAPI()
1717

@@ -42,3 +42,4 @@ def server_request_hook(span: Span, scope: dict):
4242
app.include_router(foodies.router)
4343
app.include_router(files.router)
4444
app.include_router(speeches.router)
45+
app.include_router(chats.router)

template_fastapi/routers/chats.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
2+
from fastapi.responses import HTMLResponse
3+
4+
router = APIRouter()
5+
6+
7+
class ConnectionManager:
8+
def __init__(self):
9+
self.active_connections: list[WebSocket] = []
10+
11+
async def connect(self, websocket: WebSocket):
12+
await websocket.accept()
13+
self.active_connections.append(websocket)
14+
15+
def disconnect(self, websocket: WebSocket):
16+
self.active_connections.remove(websocket)
17+
18+
async def send_personal_message(self, message: str, websocket: WebSocket):
19+
await websocket.send_text(message)
20+
21+
async def broadcast(self, message: str):
22+
for connection in self.active_connections:
23+
await connection.send_text(message)
24+
25+
26+
manager = ConnectionManager()
27+
28+
29+
@router.get(
30+
"/chats/",
31+
tags=["chats"],
32+
)
33+
async def get():
34+
# read the HTML file
35+
try:
36+
with open("template_fastapi/templates/chats.html") as file:
37+
html = file.read()
38+
except FileNotFoundError:
39+
html = "<h1>Chat page not found</h1>"
40+
except Exception as e:
41+
html = f"<h1>Error reading chat page: {str(e)}</h1>"
42+
return HTMLResponse(html)
43+
44+
45+
@router.websocket(
46+
"/ws/{client_id}",
47+
)
48+
async def websocket_endpoint(websocket: WebSocket, client_id: int):
49+
await manager.connect(websocket)
50+
try:
51+
while True:
52+
data = await websocket.receive_text()
53+
await manager.send_personal_message(f"You wrote: {data}", websocket)
54+
await manager.broadcast(f"Client #{client_id} says: {data}")
55+
except WebSocketDisconnect:
56+
manager.disconnect(websocket)
57+
await manager.broadcast(f"Client #{client_id} left the chat")
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Chat</title>
5+
</head>
6+
<body>
7+
<h1>WebSocket Chat</h1>
8+
<h2>Your ID: <span id="ws-id"></span></h2>
9+
<form action="" onsubmit="sendMessage(event)">
10+
<input type="text" id="messageText" autocomplete="off"/>
11+
<button>Send</button>
12+
</form>
13+
<ul id='messages'>
14+
</ul>
15+
<script>
16+
var client_id = Date.now()
17+
document.querySelector("#ws-id").textContent = client_id;
18+
var ws = new WebSocket(`ws://localhost:8000/ws/${client_id}`);
19+
ws.onmessage = function(event) {
20+
var messages = document.getElementById('messages')
21+
var message = document.createElement('li')
22+
var content = document.createTextNode(event.data)
23+
message.appendChild(content)
24+
messages.appendChild(message)
25+
};
26+
function sendMessage(event) {
27+
var input = document.getElementById("messageText")
28+
ws.send(input.value)
29+
input.value = ''
30+
event.preventDefault()
31+
}
32+
</script>
33+
</body>
34+
</html>

uv.lock

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

0 commit comments

Comments
 (0)