add events table

This commit is contained in:
2025-05-29 13:57:28 +02:00
parent 5ecb8c956c
commit 3174f8fa5b
8 changed files with 258 additions and 1 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -29,6 +29,20 @@ flask db init
flask db migrate -m "Initial migration"
flask db upgrade
# Create events table
echo "Creating events table..."
python3 -c "
from migrations.add_events_table import upgrade
from app import create_app
app = create_app()
with app.app_context():
try:
upgrade()
print('Events table created successfully')
except Exception as e:
print(f'Error creating events table: {e}')
"
# Create default site settings if they don't exist
echo "Creating default site settings..."
python3 -c "

View File

@@ -0,0 +1,61 @@
import os
import sys
from pathlib import Path
# Add the parent directory to Python path so we can import from root
sys.path.append(str(Path(__file__).parent.parent))
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from extensions import db
from sqlalchemy import text
def upgrade():
# Create events table
with db.engine.connect() as conn:
conn.execute(text('''
CREATE TABLE IF NOT EXISTS events (
id SERIAL PRIMARY KEY,
event_type VARCHAR(50) NOT NULL,
user_id INTEGER NOT NULL REFERENCES "user" (id),
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
details JSONB,
ip_address VARCHAR(45),
user_agent VARCHAR(255)
);
-- Create index on event_type for faster filtering
CREATE INDEX IF NOT EXISTS idx_events_event_type ON events(event_type);
-- Create index on timestamp for faster date-based queries
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
-- Create index on user_id for faster user-based queries
CREATE INDEX IF NOT EXISTS idx_events_user_id ON events(user_id);
'''))
conn.commit()
def downgrade():
# Drop events table and its indexes
with db.engine.connect() as conn:
conn.execute(text('''
DROP INDEX IF EXISTS idx_events_event_type;
DROP INDEX IF EXISTS idx_events_timestamp;
DROP INDEX IF EXISTS idx_events_user_id;
DROP TABLE IF EXISTS events;
'''))
conn.commit()
if __name__ == '__main__':
app = Flask(__name__)
# Use the same database configuration as in app.py
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'postgresql://postgres:1253@localhost:5432/docupulse')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
print("Connecting to database...")
db.init_app(app)
with app.app_context():
upgrade()

View File

@@ -4,6 +4,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
from sqlalchemy.orm import relationship
from extensions import db
from enum import Enum
# Association table for room members
room_members = db.Table('room_members',
@@ -201,4 +202,62 @@ class MessageAttachment(db.Model):
message = db.relationship('Message', back_populates='attachments')
def __repr__(self):
return f'<MessageAttachment {self.name}>'
return f'<MessageAttachment {self.name}>'
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'<Event {self.event_type} by User {self.user_id} at {self.timestamp}>'

123
utils/event_logger.py Normal file
View File

@@ -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()