From ed7c3f0607a657af70cca8925f2105f0ded76c03 Mon Sep 17 00:00:00 2001 From: rasmus Date: Fri, 8 Aug 2025 00:10:02 +0300 Subject: [PATCH] Move /swipe from inventory-app --- app/doorboy-proxy.py | 77 ++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/app/doorboy-proxy.py b/app/doorboy-proxy.py index 69969a2..20e2af9 100755 --- a/app/doorboy-proxy.py +++ b/app/doorboy-proxy.py @@ -3,6 +3,7 @@ from datetime import date, datetime from sanic import Sanic from sanic.response import text, json from sanic_prometheus import monitor +from dateutil.parser import parse import httpx from motor.motor_asyncio import AsyncIOMotorClient from pymongo.errors import PyMongoError @@ -20,7 +21,6 @@ WORKSHOP_ACCESS_GROUP = os.environ["WORKSHOP_ACCESS_GROUP"] MONGO_URI = os.environ["MONGO_URI"] INVENTORY_API_KEY = os.environ["INVENTORY_API_KEY"] # asshole forwards to inventory-app, instead of using mongo directly CARD_URI = os.environ["CARD_URI"] -SWIPE_URI = os.environ["SWIPE_URI"] assert len(DOORBOY_SECRET_FLOOR) >= 10 assert len(DOORBOY_SECRET_WORKSHOP) >= 10 @@ -132,8 +132,9 @@ async def view_longpoll(request): await response.send("data: response-generator-ended\n\n") return +# Called by the door to log a card swipe. Does not decide whether the door should be opened. @app.post("/swipe") -async def forward_swipe(request): +async def swipe(request): # authenticate key = request.headers.get("KEY") if not key or key not in [DOORBOY_SECRET_FLOOR, DOORBOY_SECRET_WORKSHOP]: @@ -150,41 +151,47 @@ async def forward_swipe(request): print("Door", repr(data.get("door")), "not in", doors) return text("Not allowed", 403) -def slack_post(msg): - if SLACK_DOORLOG_CALLBACK == "DEV": - print(f"[DEV SLACK]: {msg}") - return - - try: - requests.post(SLACK_DOORLOG_CALLBACK, json={"text": msg }).raise_for_status() - except requests.exceptions.RequestException as e: - print(f"[SLACK]: {e}") - -def approvedStr(approved: bool) -> str: - if approved: - return "Permitted" + timestamp = parse(data["timestamp"]) if data.get("timestamp") else datetime.now(datetime.timezone.utc) - return "Denied" - -# consumes SLACK_DOORLOG_CALLBACK and app.ctx.db -@app.listener("after_server_start") -async def slack_log(app, loop): - pipeline = [{ - "$match": { - "operationType": "insert", + # Update token, create if unknown + app.ctx.db.inventory.update_one({ + "component": "doorboy", + "type": "token", + "token.uid_hash": data["uid_hash"] + }, { + "$set": { + "last_seen": timestamp + }, + "$setOnInsert": { + "first_seen": timestamp, + "inventory": { + "claimable": True, + } } - }] - while True: - try: - async with app.ctx.db.eventlog.watch(pipeline) as stream: - async for event in stream: - ev = event["fullDocument"] - - msg = "%s %s access for %s via %s" % (approvedStr(ev["approved"]), ev["door"], ev["user"]["name"], ev("method")) - slack_post(msg) - - except PyMongoError as e: - print(e) + }, upsert=True) + + token = app.ctx.db.inventory.find_one({ + "component": "doorboy", + "type": "token", + "token.uid_hash": data["uid_hash"] + }) + + event_swipe = { + "component": "doorboy", + "method": "token", + "timestamp": timestamp, + "door": data["door"], + "event": "card-swiped", + "approved": data["approved"], + "uid_hash": data["uid_hash"], + "user": { + "id": token.get("inventory", {}).get("owner", {}).get("username", ""), + "name": token.get("inventory", {}).get("owner", {}).get("display_name", "Unclaimed Token") + } + } + app.ctx.db.eventlog.insert_one(event_swipe) + + return text("ok") if __name__ == "__main__": app.run(debug=False, host="0.0.0.0", port=5000, single_process=True, access_log=True)