Harden redemption flow and improve operational safety
This commit is contained in:
@@ -1,17 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from . import db
|
||||
|
||||
|
||||
def utc_now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
def serialize_datetime(value: datetime | None) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
if value.tzinfo is None:
|
||||
value = value.replace(tzinfo=timezone.utc)
|
||||
return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
|
||||
|
||||
class RedemptionCode(db.Model):
|
||||
__tablename__ = "redemption_codes"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
code = db.Column(db.String(64), unique=True, nullable=False, index=True)
|
||||
status = db.Column(db.String(16), nullable=False, default="available")
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now())
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=utc_now)
|
||||
used_at = db.Column(db.DateTime, nullable=True)
|
||||
used_by_username = db.Column(db.String(256), nullable=True)
|
||||
used_by_principal_name = db.Column(db.String(256), nullable=True)
|
||||
@@ -21,8 +34,42 @@ class RedemptionCode(db.Model):
|
||||
"id": self.id,
|
||||
"code": self.code,
|
||||
"status": self.status,
|
||||
"createdAt": self.created_at.isoformat() if self.created_at else None,
|
||||
"usedAt": self.used_at.isoformat() if self.used_at else None,
|
||||
"createdAt": serialize_datetime(self.created_at),
|
||||
"usedAt": serialize_datetime(self.used_at),
|
||||
"usedByUsername": self.used_by_username,
|
||||
"usedByPrincipalName": self.used_by_principal_name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AuditEvent(db.Model):
|
||||
__tablename__ = "audit_events"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
event_type = db.Column(db.String(64), nullable=False, index=True)
|
||||
status = db.Column(db.String(16), nullable=False, default="success", index=True)
|
||||
actor = db.Column(db.String(128), nullable=False, default="system")
|
||||
code = db.Column(db.String(64), nullable=True, index=True)
|
||||
username = db.Column(db.String(256), nullable=True)
|
||||
principal_name = db.Column(db.String(256), nullable=True)
|
||||
details = db.Column(db.Text, nullable=True)
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=utc_now, index=True)
|
||||
|
||||
def to_dict(self):
|
||||
parsed_details = None
|
||||
if self.details:
|
||||
try:
|
||||
parsed_details = json.loads(self.details)
|
||||
except ValueError:
|
||||
parsed_details = {"raw": self.details}
|
||||
|
||||
return {
|
||||
"id": self.id,
|
||||
"eventType": self.event_type,
|
||||
"status": self.status,
|
||||
"actor": self.actor,
|
||||
"code": self.code,
|
||||
"username": self.username,
|
||||
"principalName": self.principal_name,
|
||||
"details": parsed_details,
|
||||
"createdAt": serialize_datetime(self.created_at),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user