Skip to content

Commit

Permalink
put webpage in fastapi template (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
gounux authored Jul 23, 2024
1 parent 16da7f1 commit 8ba2f6f
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 77 deletions.
14 changes: 9 additions & 5 deletions gischat/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import sys

import colorlog
from fastapi import FastAPI, HTTPException, WebSocket
from fastapi import FastAPI, HTTPException, Request, WebSocket
from fastapi.encoders import jsonable_encoder
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from starlette.websockets import WebSocketDisconnect

from gischat.models import MessageModel, RulesModel, StatusModel, VersionModel
from gischat.utils import get_poetry_version
from gischat.ws_html import ws_html

# logger
logger = logging.getLogger()
Expand Down Expand Up @@ -74,11 +74,15 @@ async def notify(self, room: str, message: str):
summary="Chat with your GIS tribe in QGIS, QField and other clients !",
version=get_poetry_version(),
)
templates = Jinja2Templates(directory="gischat/templates")


@app.get("/")
async def get_ws_page():
return HTMLResponse(ws_html)
@app.get("/", response_class=HTMLResponse)
async def get_ws_page(request: Request):
return templates.TemplateResponse(
request=request,
name="ws-page.html",
)


@app.get("/version", response_model=VersionModel)
Expand Down
121 changes: 121 additions & 0 deletions gischat/templates/ws-page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>gischat</title>
</head>
<body>
<h1>gischat websocket client</h1>
<form action="" onsubmit="sendMessage(event)">
<label>Instance: <input type="text" id="instance" autocomplete="off" value=""/></label>
<label>SSL: <input type="checkbox" id="ssl" checked/></label>
<button onclick="onRulesButtonClick(event)">Rules</button>
<button onclick="onStatusButtonClick(event)">Status</button>
<hr>
<label>Room: <input type="text" id="roomId" autocomplete="off" value=""/></label>
<button onclick="onDetectButtonClick(event)">Detect</button>
<button id="connectButton" onclick="onConnectButtonClick(event)">Connect</button>
<hr>
<label>Author: <input type="text" id="authorId" autocomplete="off" value=""/></label>
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
<button>Send</button>
</form>
<hr>
<ul id='messages'>
</ul>
<script>
let websocket = null;
let connected = false;
const instance = document.getElementById('instance');
instance.value = window.location.host;
const ssl = document.getElementById("ssl");
ssl.checked = window.location.protocol.startsWith("https");
const connectButton = document.getElementById('connectButton');

function displayMessage(msg) {
const messages = document.getElementById('messages');
const message = document.createElement('li');
console.log(msg);
const content = document.createTextNode(msg);
message.appendChild(content);
messages.appendChild(message);
}

function onRulesButtonClick(event) {
const protocol = ssl.checked ? "https" : "http";
fetch(`${protocol}://${instance.value}/rules`)
.then(response => response.json())
.then(data => displayMessage(`Instance rules: ${data.rules}`));
event.preventDefault();
}

function onStatusButtonClick(event) {
const protocol = ssl.checked ? "https" : "http";
fetch(`${protocol}://${instance.value}/status`)
.then(response => response.json())
.then(data => displayMessage(`Instance status: ${data.status} - users in rooms: ${data.rooms.map(room => `'${room.name}': ${room.nb_connected_users}`).join(",")}`));
event.preventDefault();
}

function onDetectButtonClick(event) {
const protocol = ssl.checked ? "https" : "http";
fetch(`${protocol}://${instance.value}/rooms`)
.then(response => response.json())
.then(data => displayMessage(`Available rooms: ${data.join(',')}`));
event.preventDefault();
}

function onConnectButtonClick(event) {
if (connected) {
websocket.close();
event.preventDefault();
return;
}
const room = document.getElementById("roomId");
if (!room.value) {
alert("Room must be set");
return;
}
const ws_protocol = ssl.checked ? "wss" : "ws";
const ws_url = `${ws_protocol}://${instance.value}/room/${room.value}/ws`;
websocket = new WebSocket(ws_url);
websocket.onopen = (event) => {
displayMessage(`Connected to websocket in room ${room.value}`);
connected = true;
connectButton.innerText = "Disconnect";
}
websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
const log = `[${data.author}] (${new Date().toLocaleTimeString()}): ${data.message}`;
displayMessage(log);
};
websocket.onerror = (error) => {
displayMessage(`Websocket error ${JSON.stringify(error)}`);
console.log("Websocket error", error);
};
websocket.onclose = (event) => {
connected = false;
connectButton.innerText = "Connect";
displayMessage("Disconnected from websocket");
}
event.preventDefault();
}

function sendMessage(event) {
if (!connected) {
displayMessage("Not connected to websocket");
event.preventDefault();
return;
}
const message = document.getElementById("messageText");
const author = document.getElementById("authorId");
if (!message.value || !author.value) {
alert("Author and message can not be empty");
return;
}
websocket.send(JSON.stringify({message: message.value, author: author.value}));
message.value = '';
event.preventDefault();
}
</script>
</body>
</html>
71 changes: 0 additions & 71 deletions gischat/ws_html.py

This file was deleted.

2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ colorlog = "^6.8.2"
websockets = "^12.0"
pydantic = "^2.8.2"
toml = "^0.10.2"
jinja2 = "^3.1.4"

[tool.poetry.group.dev.dependencies]
pre-commit = "^3.7.1"
Expand Down

0 comments on commit 8ba2f6f

Please sign in to comment.