initial commit
This commit is contained in:
commit
3fa81f0ae3
7 changed files with 129 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.mypy_cache/
|
||||
__pycache__/
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -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)
|
15
pizzicore/Dockerfile
Normal file
15
pizzicore/Dockerfile
Normal file
|
@ -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"]
|
6
pizzicore/README.md
Normal file
6
pizzicore/README.md
Normal file
|
@ -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.
|
10
pizzicore/docker-compose.yaml
Normal file
10
pizzicore/docker-compose.yaml
Normal file
|
@ -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
|
89
pizzicore/pizzicore.py
Normal file
89
pizzicore/pizzicore.py
Normal file
|
@ -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))
|
2
pizzicore/requirements.txt
Normal file
2
pizzicore/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
fastapi==0.62.0
|
||||
uvicorn==0.13.1
|
Loading…
Reference in a new issue