Skip to content

Commit 9cfd31e

Browse files
Copilotks6088ts
andcommitted
Implement configurable WebSocket URL for chats service
Co-authored-by: ks6088ts <[email protected]>
1 parent 459b3f2 commit 9cfd31e

File tree

5 files changed

+86
-39
lines changed

5 files changed

+86
-39
lines changed

.env.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ AZURE_BLOB_STORAGE_CONTAINER_NAME="files"
1717
# Azure AI Speech
1818
AZURE_AI_SPEECH_API_KEY="<YOUR_AZURE_AI_SPEECH_API_KEY>"
1919
AZURE_AI_SPEECH_ENDPOINT="https://<speech-api-name>.cognitiveservices.azure.com/"
20+
21+
# Chats WebSocket
22+
CHATS_WEBSOCKET_URL="ws://localhost:8000"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from fastapi import WebSocket
2+
3+
4+
class ConnectionManager:
5+
"""WebSocket connection manager for handling multiple client connections."""
6+
7+
def __init__(self):
8+
self.active_connections: list[WebSocket] = []
9+
10+
async def connect(self, websocket: WebSocket):
11+
"""Accept a WebSocket connection and add it to active connections."""
12+
await websocket.accept()
13+
self.active_connections.append(websocket)
14+
15+
def disconnect(self, websocket: WebSocket):
16+
"""Remove a WebSocket connection from active connections."""
17+
self.active_connections.remove(websocket)
18+
19+
async def send_personal_message(self, message: str, websocket: WebSocket):
20+
"""Send a personal message to a specific WebSocket connection."""
21+
await websocket.send_text(message)
22+
23+
async def broadcast(self, message: str):
24+
"""Broadcast a message to all active connections."""
25+
for connection in self.active_connections:
26+
await connection.send_text(message)
27+
28+
29+
class ChatRepository:
30+
"""Repository for handling chat-related operations."""
31+
32+
def __init__(self):
33+
self.manager = ConnectionManager()
34+
35+
async def handle_client_message(self, data: str, websocket: WebSocket, client_id: int):
36+
"""Handle a message from a client."""
37+
await self.manager.send_personal_message(f"You wrote: {data}", websocket)
38+
await self.manager.broadcast(f"Client #{client_id} says: {data}")
39+
40+
async def handle_client_disconnect(self, client_id: int):
41+
"""Handle a client disconnect."""
42+
await self.manager.broadcast(f"Client #{client_id} left the chat")

template_fastapi/routers/chats.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,41 @@
1-
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
1+
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Request
22
from fastapi.responses import HTMLResponse
3+
from fastapi.templating import Jinja2Templates
34

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)
5+
from template_fastapi.repositories.chats import ChatRepository
6+
from template_fastapi.settings.chats import get_chats_settings
177

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()
8+
router = APIRouter()
9+
templates = Jinja2Templates(directory="template_fastapi/templates")
10+
chat_repository = ChatRepository()
2711

2812

2913
@router.get(
3014
"/chats/",
3115
tags=["chats"],
3216
)
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)
17+
async def get(request: Request):
18+
"""Get the chat page with configurable WebSocket URL."""
19+
settings = get_chats_settings()
20+
return templates.TemplateResponse(
21+
"chats.html",
22+
{
23+
"request": request,
24+
"websocket_url": settings.chats_websocket_url,
25+
},
26+
)
4327

4428

4529
@router.websocket(
4630
"/ws/{client_id}",
4731
)
4832
async def websocket_endpoint(websocket: WebSocket, client_id: int):
49-
await manager.connect(websocket)
33+
"""WebSocket endpoint for chat functionality."""
34+
await chat_repository.manager.connect(websocket)
5035
try:
5136
while True:
5237
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}")
38+
await chat_repository.handle_client_message(data, websocket, client_id)
5539
except WebSocketDisconnect:
56-
manager.disconnect(websocket)
57-
await manager.broadcast(f"Client #{client_id} left the chat")
40+
chat_repository.manager.disconnect(websocket)
41+
await chat_repository.handle_client_disconnect(client_id)

template_fastapi/settings/chats.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from functools import lru_cache
2+
3+
from pydantic_settings import BaseSettings, SettingsConfigDict
4+
5+
6+
class Settings(BaseSettings):
7+
chats_websocket_url: str = "ws://localhost:8000"
8+
9+
model_config = SettingsConfigDict(
10+
env_file=".env",
11+
env_ignore_empty=True,
12+
extra="ignore",
13+
)
14+
15+
16+
@lru_cache
17+
def get_chats_settings() -> Settings:
18+
return Settings()

template_fastapi/templates/chats.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ <h2>Your ID: <span id="ws-id"></span></h2>
1515
<script>
1616
var client_id = Date.now()
1717
document.querySelector("#ws-id").textContent = client_id;
18-
var ws = new WebSocket(`ws://localhost:8000/ws/${client_id}`);
18+
var ws = new WebSocket(`{{ websocket_url }}/ws/${client_id}`);
1919
ws.onmessage = function(event) {
2020
var messages = document.getElementById('messages')
2121
var message = document.createElement('li')

0 commit comments

Comments
 (0)