Implement State WebSocket

This commit is contained in:
Kai Vogelgesang 2022-01-02 02:08:58 +01:00
parent 445587e596
commit 2dec48a004
Signed by: kai
GPG Key ID: 0A95D3B6E62C0879
9 changed files with 161 additions and 21 deletions

2
backend/.gitignore vendored
View File

@ -1,3 +1,5 @@
db.json
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

View File

@ -9,6 +9,7 @@ uvicorn = "==0.15"
python-dotenv = "*"
python-jose = {extras = ["cryptography"], version = "*"}
tinydb = "*"
websockets = "*"
[dev-packages]
httpx = "*"

59
backend/Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "f0775dc9b11be56fda53dbb2a263aeccbf591a807be967b788b6edee291b4729"
"sha256": "787b11dfd8e6c14187dac5e802e341440802b59aee7df4c21767656734f56773"
},
"pipfile-spec": 6,
"requires": {
@ -286,12 +286,67 @@
"version": "==4.0.1"
},
"uvicorn": {
"extras": [],
"hashes": [
"sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1",
"sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"
],
"index": "pypi",
"version": "==0.15"
},
"websockets": {
"hashes": [
"sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657",
"sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c",
"sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485",
"sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c",
"sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5",
"sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9",
"sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d",
"sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4",
"sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f",
"sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b",
"sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053",
"sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc",
"sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519",
"sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127",
"sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a",
"sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37",
"sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909",
"sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530",
"sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155",
"sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f",
"sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be",
"sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534",
"sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72",
"sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd",
"sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db",
"sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f",
"sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e",
"sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c",
"sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7",
"sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e",
"sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a",
"sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb",
"sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0",
"sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123",
"sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6",
"sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8",
"sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17",
"sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9",
"sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536",
"sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7",
"sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69",
"sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c",
"sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54",
"sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89",
"sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60",
"sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d",
"sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512",
"sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58"
],
"index": "pypi",
"version": "==10.1"
}
},
"develop": {
@ -315,7 +370,7 @@
"sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721",
"sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"
],
"markers": "python_full_version >= '3.5.0'",
"markers": "python_version >= '3.5'",
"version": "==2.0.9"
},
"h11": {

View File

@ -2,8 +2,32 @@ from jose import jwt
from settings import settings
def encode(data):
return jwt.encode(data, settings.secret_key)
def decode(token):
return jwt.decode(token, settings.secret_key)
def validate_frontend(token):
try:
data = decode(token)
assert data["type"] == "frontend"
return True
except Exception as e:
print(e)
return False
def validate_computer(token):
try:
data = decode(token)
assert data["type"] == "computer"
return True
except Exception as e:
print(e)
return False

View File

@ -1,27 +1,34 @@
from fastapi import FastAPI, Request, Response, WebSocket
from fastapi import FastAPI, Request, Response, WebSocket, WebSocketDisconnect
from settings import settings
import auth
from state import StateManager
app = FastAPI()
state_manager = StateManager()
@app.get("/api/validate")
@app.get("/api/{token}/validate")
async def validate_token(token: str):
return {"success": auth.validate_frontend(token)}
result = None
@app.websocket("/api/{token}/state")
async def state_updates_websocket(websocket: WebSocket, token: str):
if not auth.validate_frontend(token):
await websocket.close()
return
await websocket.accept()
await state_manager.on_connect(websocket)
try:
data = auth.decode(token)
assert data["type"] == "frontend"
result = True
except Exception as e:
result = False
return {"success": result}
while True:
await websocket.receive_json()
except WebSocketDisconnect:
await state_manager.on_disconnect(websocket)
if settings.dev_mode:

View File

@ -6,6 +6,7 @@ class Settings(BaseSettings):
dev_npm_port: int = 3000
frontend_path: str = "frontend"
database_path: str = "db.json"
secret_key: str

33
backend/state.py Normal file
View File

@ -0,0 +1,33 @@
import asyncio
from tinydb import TinyDB
from settings import settings
db = TinyDB(settings.database_path)
computers = db.table("computers")
class StateManager:
def __init__(self):
self.websockets = set()
self.current_state = None
self.update_state()
def update_state(self):
self.current_state = {"computers": computers.all()}
async def push_state(self, socket):
await socket.send_json(self.current_state)
async def on_connect(self, socket):
self.websockets.add(socket)
await self.push_state(socket)
async def on_disconnect(self, socket):
self.websockets.remove(socket)
async def on_change(self):
self.update_state()
await asyncio.gather(self.push_state(socket) for socket in self.websockets)

View File

@ -1,13 +1,33 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { State } from "../proto";
import { TokenContext } from "../tokenStorage";
export const Index: React.FC = () => {
const tokenStorage = React.useContext(TokenContext);
const [state, setState] = useState<State>();
useEffect(() => {
const url = new URL(`api/${tokenStorage.token}/state`, window.location.href);
url.protocol = url.protocol.replace("http", "ws");
const socket = new WebSocket(url.href);
socket.onmessage = (e) => {
let newState = JSON.parse(e.data) as State;
setState(newState);
}
return () => { socket.close(); };
}, [tokenStorage.token]);
return <>
Hallo i bims 1 index u d1 token bimst {tokenStorage.token}
<p>Hallo i bims 1 index u d1 token bimst {tokenStorage.token}</p>
<button onClick={tokenStorage.reset}>Resetteroni</button>
<p>Der State ist:</p>
{JSON.stringify(state)}
</>
}

View File

@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from "react";
import Login from "./Login";
const LOCAL_STORAGE_KEY = "token";
const VALIDATE_ENDPOINT = "api/validate";
export type Token = string | null;
export type TokenStorage = {
@ -37,9 +36,7 @@ export const TokenProvider: React.FC = (props) => {
useEffect(() => {
(async () => {
if (tokenStorage.token) {
const response = await fetch(`${VALIDATE_ENDPOINT}?` + new URLSearchParams({
token: tokenStorage.token,
}));
const response = await fetch(`api/${token}/validate`);
const data = await response.json() as { "success": boolean };