Skip to content

Commit c00ea54

Browse files
authored
Merge pull request #24 from ks6088ts-labs/copilot/fix-23
Make WebSocket URL configurable for chats service
2 parents e4e96d2 + ba9abaa commit c00ea54

File tree

5 files changed

+86
-40
lines changed

5 files changed

+86
-40
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 & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,40 @@
1-
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
2-
from fastapi.responses import HTMLResponse
1+
from fastapi import APIRouter, Request, WebSocket, WebSocketDisconnect
2+
from fastapi.templating import Jinja2Templates
33

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

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

2811

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

4427

4528
@router.websocket(
4629
"/ws/{client_id}",
4730
)
4831
async def websocket_endpoint(websocket: WebSocket, client_id: int):
49-
await manager.connect(websocket)
32+
"""WebSocket endpoint for chat functionality."""
33+
await chat_repository.manager.connect(websocket)
5034
try:
5135
while True:
5236
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}")
37+
await chat_repository.handle_client_message(data, websocket, client_id)
5538
except WebSocketDisconnect:
56-
manager.disconnect(websocket)
57-
await manager.broadcast(f"Client #{client_id} left the chat")
39+
chat_repository.manager.disconnect(websocket)
40+
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)