Fleet alerts push server notifications to endpoint PCs via the command pipe. Operators acknowledge alerts via toast UI in the tray. Backend tracks acknowledgements per asset to prevent re-showing dismissed alerts. Backend: alertfleet/alertscope/alerttimeout columns, GET/POST /api/endpoints/alerts, NotificationAcknowledgement model with unique constraint, hostname validation. Frontend: Alert Fleet checkbox in notification form. Agent: FleetAlertClient polls alerts, FleetAlertStore tracks acknowledged IDs, FleetAlertForm displays toast (no auto-dismiss), FleetAlertWorker 60s polling. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
"""Notification acknowledgement tracking for fleet alerts."""
|
|
|
|
from datetime import datetime, timezone
|
|
from shopdb.api import db
|
|
|
|
|
|
class NotificationAcknowledgement(db.Model):
|
|
"""
|
|
Tracks which assets have acknowledged fleet alerts.
|
|
Matches existing notificationacknowledgements table.
|
|
"""
|
|
__tablename__ = 'notificationacknowledgements'
|
|
__table_args__ = (
|
|
db.UniqueConstraint('notificationid', 'assetid', name='uq_notification_asset'),
|
|
)
|
|
|
|
notificationacknowledgementid = db.Column(db.Integer, primary_key=True)
|
|
notificationid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('notifications.notificationid', ondelete='CASCADE'),
|
|
nullable=False,
|
|
index=True
|
|
)
|
|
assetid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('assets.assetid', ondelete='CASCADE'),
|
|
nullable=False,
|
|
index=True
|
|
)
|
|
acknowledgedat = db.Column(
|
|
db.DateTime,
|
|
nullable=False,
|
|
default=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
|
|
)
|
|
|
|
# Relationships
|
|
notification = db.relationship('Notification', backref='acknowledgements')
|
|
asset = db.relationship('Asset', backref='notification_acknowledgements')
|
|
|
|
def __repr__(self):
|
|
return f"<NotificationAcknowledgement {self.notificationacknowledgementid}>"
|
|
|
|
def to_dict(self):
|
|
"""Convert to dictionary following ShopDB patterns."""
|
|
return {
|
|
'notificationacknowledgementid': self.notificationacknowledgementid,
|
|
'notificationid': self.notificationid,
|
|
'assetid': self.assetid,
|
|
'acknowledgedat': self.acknowledgedat.replace(
|
|
tzinfo=timezone.utc
|
|
).isoformat() if self.acknowledgedat else None
|
|
}
|