From 3fa81f0ae357c06584e8448e75c892fa7faa5ad3 Mon Sep 17 00:00:00 2001 From: boyska Date: Thu, 16 Sep 2021 01:47:11 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + README.md | 5 ++ pizzicore/Dockerfile | 15 ++++++ pizzicore/README.md | 6 +++ pizzicore/docker-compose.yaml | 10 ++++ pizzicore/pizzicore.py | 89 +++++++++++++++++++++++++++++++++++ pizzicore/requirements.txt | 2 + 7 files changed, 129 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pizzicore/Dockerfile create mode 100644 pizzicore/README.md create mode 100644 pizzicore/docker-compose.yaml create mode 100644 pizzicore/pizzicore.py create mode 100644 pizzicore/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb176dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.mypy_cache/ +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..8abc2f8 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +feature che vorremmo: + - interfaccia che mostra in grande dei numeri + - possibilità di controllo da tastiera, ma magari anche in altri modi + - possibilità di sapere lo stato corrente (HTTP) + - notifiche push (websocket) diff --git a/pizzicore/Dockerfile b/pizzicore/Dockerfile new file mode 100644 index 0000000..4e46cdb --- /dev/null +++ b/pizzicore/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.7 + +ENV DEBIAN_FRONTEND=noninteractive + +WORKDIR /src + +RUN apt-get update + +RUN python -m pip install wheel + +COPY requirements.txt / +RUN python -m pip install -r /requirements.txt +COPY . /src/ + +CMD ["uvicorn", "pizzicore:app", "--reload", "--port", "8000", "--host", "0.0.0.0"] diff --git a/pizzicore/README.md b/pizzicore/README.md new file mode 100644 index 0000000..3185a30 --- /dev/null +++ b/pizzicore/README.md @@ -0,0 +1,6 @@ +Componente centrale del sistema pizzicaroli. tiene i conti. +espone HTTP e WebSocket. + +Alcune API richiedono autenticazione. + +Il docker-compose incluso è solo per comodità di sviluppo, non è strettamente necessario. diff --git a/pizzicore/docker-compose.yaml b/pizzicore/docker-compose.yaml new file mode 100644 index 0000000..811aa62 --- /dev/null +++ b/pizzicore/docker-compose.yaml @@ -0,0 +1,10 @@ +version: "3" + +services: + server: + build: . + # XXX: prima o poi servirà una qualche /var/lib/ in cui tenere il contatore + volumes: + - .:/src/ + ports: + - 8000:8000 diff --git a/pizzicore/pizzicore.py b/pizzicore/pizzicore.py new file mode 100644 index 0000000..c04a4b6 --- /dev/null +++ b/pizzicore/pizzicore.py @@ -0,0 +1,89 @@ +import secrets +from collections import defaultdict + +from asyncio.queues import Queue +from fastapi import FastAPI, WebSocket, HTTPException, Depends, status +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from pydantic import BaseModel + + +class Store: + def __init__(self, n: int): + self.values = {i: 0 for i in range(n)} + + def get(self, key): + return self.values[key] + + def incr(self, key): + return self.set(key, self.get(key) + 1) + + def set(self, key, value): + self.values[key] = value + return value + + +class SignalStore(Store): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.registry = defaultdict(list) + + def subscribe(self, key, q): + self.registry[key].append(q) + + def _notify(self, key): + for queue in self.registry[key]: + queue.put_nowait(self.get(key)) + + def set(self, key, value): + super().set(key, value) + self._notify(key) + + +app = FastAPI() +counter_store = Store(n=1) # XXX: pesca da file di conf +security = HTTPBasic() + + +class Value(BaseModel): + counter: int + value: int + + +def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): + # XXX: read user/pass from config + correct_username = secrets.compare_digest(credentials.username, "avanti") + correct_password = secrets.compare_digest(credentials.password, "prossimo") + if not (correct_username and correct_password): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Basic"}, + ) + return "admin" + + +@app.get("/counter/{cid}") +async def get_value(cid: int): + try: + val = counter_store.get(cid) + except KeyError: + raise HTTPException(status_code=404, detail="Item not found") + return Value(counter=cid, value=val) + + +@app.post("/counter/{cid}/increment") +async def increment(cid: int, role: str = Depends(get_current_username)): + if role != "admin": + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + val = counter_store.incr(cid) + return Value(counter=cid, value=val) + + +@app.websocket("/ws/counter/{cid}") +async def websocket_counter(websocket: WebSocket, cid: int): + await websocket.accept() + # XXX: subscribe to counter + while True: + # XXX: get notifications + val = 1 + await websocket.send_text(str(val)) diff --git a/pizzicore/requirements.txt b/pizzicore/requirements.txt new file mode 100644 index 0000000..867e5ef --- /dev/null +++ b/pizzicore/requirements.txt @@ -0,0 +1,2 @@ +fastapi==0.62.0 +uvicorn==0.13.1