From 3174f8fa5b7aada525452ad3208a80cccb9fdedf Mon Sep 17 00:00:00 2001 From: Kobe Date: Thu, 29 May 2025 13:57:28 +0200 Subject: [PATCH] add events table --- __pycache__/app.cpython-313.pyc | Bin 5179 -> 5477 bytes __pycache__/models.cpython-313.pyc | Bin 17774 -> 20607 bytes entrypoint.sh | 14 ++ migrations/add_events_table.py | 61 +++++++++ models.py | 61 ++++++++- .../__pycache__/conversations.cpython-313.pyc | Bin 20141 -> 20141 bytes routes/__pycache__/main.cpython-313.pyc | Bin 35731 -> 35731 bytes utils/event_logger.py | 123 ++++++++++++++++++ 8 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 migrations/add_events_table.py create mode 100644 utils/event_logger.py diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index 45ae237ad821083ad13f887a5b145fe790693bea..293a4a7d94e827fd1f0885cebd0175fd77ff1168 100644 GIT binary patch delta 1272 zcmY*XO>7%Q6rR~%|HQvrJB<^&n>3A+Hg+AyQI#lai$WE}O z3z`a~&_gBAQzbZ|{s=fA6_Eo-z4X*d91v+MRa!WJfD;E23{r3KCYw#1k>-1E-uvD+ z@6GPr{yz@Kn;wsg;Ckocv5Sc|LVn{1dr8|)bMWK%oqnCnDLrIlJEjxE8zC$@7u}7q z*jy}k0-h5!2+?prpX)XPW9JDe`U%0zDV~vohBiv!JpGo6;1@c&O7?Bs%D6?`&2hZ@ zf(kQ2OjuH3Nf;DnRiI)Bz7|y5{XrPA+-YvkiaIy1ikOWZA9Rbgjki-4Xa{r za#kIoR#+$bkmYeo9(%NwT~;OeFkBO3-qsSs1G1Fyw}xibTo<2g0^h@aohs!~TXEN( zQ!~`?MaU(`4pYd7?R+lAbrSv-9nlCF!PEY%vnuK2oYaGz?)Hy&9E4vbE!1l(7@Dzf zRM3&Nm5I9do;B!_U3+3fa+i0H;2;DPjUMHVbcXDV5pHQoM)4Nbq4pit{uY*!PuWc& zKfR@z;i~SAI3{M*UNR7Y3FW~)(>-Arh38M@jH$C{-j;`wsbmUV>R(6fdT#lzsNrrN zMmSEdll=BqL@AOJxbS5NleGeAwHRIAQc76t?sMLy&2gHf@#yj3T_^$KXa=BRatEa?V#48)a5_`7KkPudsT9r7_HgA?;bFC*YRn zc6TS^zrN`x6spCga-m=fwYsS+lpE#h>suqhE8Zx6nycOzeC2)fMTe;_RTdbw%(}T& zABO~oqZ~3ECOI5Js5=mJYn`;lckRy0UcfAW-}-%gpQrGKuhH?d-t&d?rt{0hckPe# zXI3UwvsbbozPm0(;ZNUq`WM;pM2@V>kxhZhsisJj&JFHuC`55unSbyc-^s++X8hME z-48znI^0>jicNEvf&T(2`2xCm$ONb8L3l4XOtbJ!@G4EixOSwSx3)EcDYM#ZI4mc# zDR^J&p*8qi8`F~OB)O>&+V+9xZ|PY|eVfzJ)Y4&fb@IyON4+bP52f}Ekw_7C089S| DRV@50 delta 1073 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' \ No newline at end of file + return f'' + +class EventType(Enum): + # User events + USER_LOGIN = 'user_login' + USER_LOGOUT = 'user_logout' + USER_CREATE = 'user_create' + USER_UPDATE = 'user_update' + USER_DELETE = 'user_delete' + + # Room events + ROOM_CREATE = 'room_create' + ROOM_UPDATE = 'room_update' + ROOM_DELETE = 'room_delete' + ROOM_MEMBER_ADD = 'room_member_add' + ROOM_MEMBER_REMOVE = 'room_member_remove' + ROOM_PERMISSION_UPDATE = 'room_permission_update' + + # File events + FILE_UPLOAD = 'file_upload' + FILE_DOWNLOAD = 'file_download' + FILE_DELETE = 'file_delete' + FILE_RENAME = 'file_rename' + FILE_MOVE = 'file_move' + FILE_STAR = 'file_star' + FILE_UNSTAR = 'file_unstar' + + # Conversation events + CONVERSATION_CREATE = 'conversation_create' + CONVERSATION_UPDATE = 'conversation_update' + CONVERSATION_DELETE = 'conversation_delete' + CONVERSATION_MEMBER_ADD = 'conversation_member_add' + CONVERSATION_MEMBER_REMOVE = 'conversation_member_remove' + + # Message events + MESSAGE_CREATE = 'message_create' + MESSAGE_UPDATE = 'message_update' + MESSAGE_DELETE = 'message_delete' + MESSAGE_ATTACHMENT_ADD = 'message_attachment_add' + MESSAGE_ATTACHMENT_REMOVE = 'message_attachment_remove' + + # Settings events + SETTINGS_UPDATE = 'settings_update' + +class Event(db.Model): + __tablename__ = 'events' + id = db.Column(db.Integer, primary_key=True) + event_type = db.Column(db.String(50), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + details = db.Column(db.JSON) # Store additional event-specific data + ip_address = db.Column(db.String(45)) # IPv6 addresses can be up to 45 chars + user_agent = db.Column(db.String(255)) + + # Relationships + user = db.relationship('User', backref='events') + + def __repr__(self): + return f'' \ No newline at end of file diff --git a/routes/__pycache__/conversations.cpython-313.pyc b/routes/__pycache__/conversations.cpython-313.pyc index 28baa3885fae1b3f57813025675f2a545410f565..bc544d10d6f37abf868f34a0587c4651e66912b6 100644 GIT binary patch delta 22 ccmZ2GmvQY}M()qNyj%=G@Yc;@BljXd08&i`&;S4c delta 22 ccmZ2GmvQY}M()qNyj%=GaQwH~M(#y^09AJfT>t<8 diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 66e2362076f75efb03ffdb7335536c9034db95e5..28d31bbb015e78197884db5123a30176c2bd1780 100644 GIT binary patch delta 21 bcmbO{ooVuPCa%xCyj%=G(CxaBtFs#bML-4_ delta 21 bcmbO{ooVuPCa%xCyj%=Ga7lb4S7$c>Mg#^j diff --git a/utils/event_logger.py b/utils/event_logger.py new file mode 100644 index 0000000..01565cb --- /dev/null +++ b/utils/event_logger.py @@ -0,0 +1,123 @@ +from flask import request +from models import Event, EventType, db +from typing import Optional, Dict, Any, List +from datetime import datetime + +def log_event( + event_type: EventType, + user_id: int, + details: Optional[Dict[str, Any]] = None +) -> Event: + """ + Log an event in the system. + + Args: + event_type: The type of event from EventType enum + user_id: The ID of the user performing the action + details: Optional dictionary containing additional event-specific data + + Returns: + The created Event object + """ + event = Event( + event_type=event_type.value, + user_id=user_id, + details=details or {}, + ip_address=request.remote_addr if request else None, + user_agent=request.user_agent.string if request and request.user_agent else None + ) + + db.session.add(event) + db.session.commit() + return event + +def get_user_events( + user_id: int, + event_type: Optional[EventType] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + limit: int = 100 +) -> List[Event]: + """ + Retrieve events for a specific user with optional filtering. + + Args: + user_id: The ID of the user to get events for + event_type: Optional event type to filter by + start_date: Optional start date to filter events + end_date: Optional end date to filter events + limit: Maximum number of events to return + + Returns: + List of Event objects matching the criteria + """ + query = Event.query.filter_by(user_id=user_id) + + if event_type: + query = query.filter_by(event_type=event_type.value) + if start_date: + query = query.filter(Event.timestamp >= start_date) + if end_date: + query = query.filter(Event.timestamp <= end_date) + + return query.order_by(Event.timestamp.desc()).limit(limit).all() + +def get_room_events( + room_id: int, + event_type: Optional[EventType] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + limit: int = 100 +) -> List[Event]: + """ + Retrieve events related to a specific room with optional filtering. + + Args: + room_id: The ID of the room to get events for + event_type: Optional event type to filter by + start_date: Optional start date to filter events + end_date: Optional end date to filter events + limit: Maximum number of events to return + + Returns: + List of Event objects matching the criteria + """ + query = Event.query.filter(Event.details['room_id'].astext.cast(Integer) == room_id) + + if event_type: + query = query.filter_by(event_type=event_type.value) + if start_date: + query = query.filter(Event.timestamp >= start_date) + if end_date: + query = query.filter(Event.timestamp <= end_date) + + return query.order_by(Event.timestamp.desc()).limit(limit).all() + +def get_recent_events( + event_type: Optional[EventType] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + limit: int = 100 +) -> List[Event]: + """ + Retrieve recent events across the system with optional filtering. + + Args: + event_type: Optional event type to filter by + start_date: Optional start date to filter events + end_date: Optional end date to filter events + limit: Maximum number of events to return + + Returns: + List of Event objects matching the criteria + """ + query = Event.query + + if event_type: + query = query.filter_by(event_type=event_type.value) + if start_date: + query = query.filter(Event.timestamp >= start_date) + if end_date: + query = query.filter(Event.timestamp <= end_date) + + return query.order_by(Event.timestamp.desc()).limit(limit).all() \ No newline at end of file