refactor slack logger
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | |||||||
| .env | .env | ||||||
| .overnodebundle | .overnodebundle | ||||||
|  | .venv | ||||||
|   | |||||||
| @@ -3,12 +3,14 @@ from datetime import date, datetime | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import text, json | from sanic.response import text, json | ||||||
| from sanic_prometheus import monitor | from sanic_prometheus import monitor | ||||||
| from motor.motor_asyncio import AsyncIOMotorClient |  | ||||||
| import httpx | import httpx | ||||||
| import pymongo | from motor.motor_asyncio import AsyncIOMotorClient | ||||||
|  | from pymongo.errors import PyMongoError | ||||||
| import os | import os | ||||||
|  | from .slack import add_slack_routes | ||||||
|  |  | ||||||
| app = Sanic(__name__) | app = Sanic(__name__) | ||||||
|  | add_slack_routes(app) | ||||||
| monitor(app).expose_endpoint() | monitor(app).expose_endpoint() | ||||||
|  |  | ||||||
| DOORBOY_SECRET_FLOOR = os.environ["DOORBOY_SECRET_FLOOR"] # API key for godoor controllers authenticating for k-space:floor | DOORBOY_SECRET_FLOOR = os.environ["DOORBOY_SECRET_FLOOR"] # API key for godoor controllers authenticating for k-space:floor | ||||||
| @@ -148,16 +150,41 @@ async def forward_swipe(request): | |||||||
|         print("Door", repr(data.get("door")), "not in", doors) |         print("Door", repr(data.get("door")), "not in", doors) | ||||||
|         return text("Not allowed", 403) |         return text("Not allowed", 403) | ||||||
|  |  | ||||||
|     async with httpx.AsyncClient() as client: | def slack_post(msg): | ||||||
|         r = await client.post(SWIPE_URI, json=data, headers={ |     if SLACK_DOORLOG_CALLBACK == "DEV": | ||||||
|             "Content-Type": "application/json", |         print(f"[DEV SLACK]: {msg}")  | ||||||
|             "Authorization": f"Basic {INVENTORY_API_KEY}" |         return | ||||||
|         }) |  | ||||||
|     if r.status_code == 200: |  | ||||||
|         return text("ok") |  | ||||||
|     else: |  | ||||||
|         return text("Failed", 500) |  | ||||||
|  |  | ||||||
|  |     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" | ||||||
|  |      | ||||||
|  |     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", | ||||||
|  |         } | ||||||
|  |     }] | ||||||
|  |     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) | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     app.run(debug=False, host="0.0.0.0", port=5000, single_process=True, access_log=True) |     app.run(debug=False, host="0.0.0.0", port=5000, single_process=True, access_log=True) | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								app/slack.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/slack.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | from pymongo.errors import PyMongoError | ||||||
|  | from requests.exceptions import RequestException | ||||||
|  | import os | ||||||
|  | import requests | ||||||
|  |  | ||||||
|  | SLACK_DOORLOG_CALLBACK = os.environ["SLACK_DOORLOG_CALLBACK"] # webhook logs to private channel or "DEV" to print to console. | ||||||
|  |  | ||||||
|  | def add_slack_routes(app): | ||||||
|  |     app.app.register_listener(slack_log, "after_server_start") # consumes SLACK_DOORLOG_CALLBACK and app.ctx.db | ||||||
|  |  | ||||||
|  | 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 RequestException as e: | ||||||
|  |         print(f"[SLACK]: {e}") | ||||||
|  |  | ||||||
|  | def approvedStr(approved: bool) -> str: | ||||||
|  |     if approved: | ||||||
|  |         return "Permitted" | ||||||
|  |      | ||||||
|  |     return "Denied" | ||||||
|  |  | ||||||
|  | async def slack_log(app, loop): | ||||||
|  |     pipeline = [{ | ||||||
|  |         "$match": { | ||||||
|  |             "operationType": "insert", | ||||||
|  |         } | ||||||
|  |     }] | ||||||
|  |     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) | ||||||
| @@ -30,6 +30,7 @@ services: | |||||||
|       FLOOR_ACCESS_GROUP: "k-space:floor" |       FLOOR_ACCESS_GROUP: "k-space:floor" | ||||||
|       WORKSHOP_ACCESS_GROUP: "k-space:workshop" |       WORKSHOP_ACCESS_GROUP: "k-space:workshop" | ||||||
|       CARD_URI: "https://inventory-app-72zn4.codemowers.ee/cards" |       CARD_URI: "https://inventory-app-72zn4.codemowers.ee/cards" | ||||||
|       SWIPE_URI: "https://inventory-app-72zn4.codemowers.ee/m/doorboy/swipe" |       SLACK_DOORLOG_CALLBACK: DEV | ||||||
|  |     env_file: .env | ||||||
|     build: |     build: | ||||||
|       context: . |       context: . | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | httpx==0.28.1 | ||||||
|  | motor==3.7.1 | ||||||
|  | pymongo==4.14.0 | ||||||
|  | python_dateutil==2.9.0 | ||||||
|  | Requests==2.32.4 | ||||||
|  | sanic==25.3.0 | ||||||
|  | sanic_prometheus==0.2.1 | ||||||
		Reference in New Issue
	
	Block a user