feat: Add hotel and webhook endpoint management
- Introduced Hotel and WebhookEndpoint models to manage hotel configurations and webhook settings. - Implemented sync_config_to_database function to synchronize hotel data from configuration to the database. - Added HotelService for accessing hotel configurations and managing customer data. - Created WebhookProcessor interface and specific processors for handling different webhook types (Wix form and generic). - Enhanced webhook processing logic to handle incoming requests and create/update reservations and customers. - Added logging for better traceability of operations related to hotels and webhooks.
This commit is contained in:
@@ -13,6 +13,7 @@ from sqlalchemy import (
|
||||
Double,
|
||||
ForeignKey,
|
||||
ForeignKeyConstraint,
|
||||
Index,
|
||||
Integer,
|
||||
String,
|
||||
)
|
||||
@@ -674,3 +675,114 @@ class ConversionRoom(Base):
|
||||
|
||||
# Relationships
|
||||
conversion = relationship("Conversion", back_populates="conversion_rooms")
|
||||
|
||||
|
||||
class Hotel(Base):
|
||||
"""Hotel configuration (migrated from alpine_bits_auth in config.yaml)."""
|
||||
|
||||
__tablename__ = "hotels"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
# Core identification
|
||||
hotel_id = Column(String(50), unique=True, nullable=False, index=True)
|
||||
hotel_name = Column(String(200), nullable=False)
|
||||
|
||||
# AlpineBits authentication
|
||||
username = Column(String(100), unique=True, nullable=False, index=True)
|
||||
password_hash = Column(String(200), nullable=False) # bcrypt
|
||||
|
||||
# Advertising accounts
|
||||
meta_account_id = Column(String(50), nullable=True)
|
||||
google_account_id = Column(String(50), nullable=True)
|
||||
|
||||
# Push endpoint (optional)
|
||||
push_endpoint_url = Column(String(500), nullable=True)
|
||||
push_endpoint_token = Column(String(200), nullable=True)
|
||||
push_endpoint_username = Column(String(100), nullable=True)
|
||||
|
||||
# Metadata
|
||||
created_at = Column(DateTime(timezone=True), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False, index=True)
|
||||
|
||||
# Relationships
|
||||
webhook_endpoints = relationship("WebhookEndpoint", back_populates="hotel")
|
||||
|
||||
|
||||
class WebhookEndpoint(Base):
|
||||
"""Webhook configurations per hotel (supports multiple webhook types per hotel)."""
|
||||
|
||||
__tablename__ = "webhook_endpoints"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
# Hotel association
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id"), nullable=False, index=True)
|
||||
|
||||
# Webhook configuration
|
||||
webhook_secret = Column(String(64), unique=True, nullable=False, index=True)
|
||||
webhook_type = Column(String(50), nullable=False) # 'wix_form', 'generic', etc.
|
||||
|
||||
# Metadata
|
||||
description = Column(String(200), nullable=True) # Human-readable label
|
||||
is_enabled = Column(Boolean, default=True, nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), nullable=False)
|
||||
|
||||
# Relationships
|
||||
hotel = relationship("Hotel", back_populates="webhook_endpoints")
|
||||
webhook_requests = relationship("WebhookRequest", back_populates="webhook_endpoint")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_webhook_endpoint_hotel_type', 'hotel_id', 'webhook_type'),
|
||||
)
|
||||
|
||||
|
||||
class WebhookRequest(Base):
|
||||
"""Tracks incoming webhooks for deduplication and retry handling."""
|
||||
|
||||
__tablename__ = "webhook_requests"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
# Request identification
|
||||
payload_hash = Column(String(64), unique=True, nullable=False, index=True) # SHA256
|
||||
webhook_endpoint_id = Column(Integer, ForeignKey("webhook_endpoints.id"), nullable=True, index=True)
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id"), nullable=True, index=True)
|
||||
|
||||
# Processing tracking
|
||||
status = Column(String(20), nullable=False, default='pending', index=True)
|
||||
# Status values: 'pending', 'processing', 'completed', 'failed'
|
||||
|
||||
processing_started_at = Column(DateTime(timezone=True), nullable=True)
|
||||
processing_completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Retry handling
|
||||
retry_count = Column(Integer, default=0)
|
||||
last_error = Column(String(2000), nullable=True)
|
||||
|
||||
# Payload storage
|
||||
payload_json = Column(JSON, nullable=True) # NULL after purge, kept for retries
|
||||
payload_size_bytes = Column(Integer, nullable=True) # Track original size
|
||||
purged_at = Column(DateTime(timezone=True), nullable=True) # When JSON was purged
|
||||
|
||||
# Metadata
|
||||
created_at = Column(DateTime(timezone=True), nullable=False, index=True)
|
||||
source_ip = Column(String(45), nullable=True)
|
||||
user_agent = Column(String(500), nullable=True)
|
||||
|
||||
# Result tracking
|
||||
created_customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True)
|
||||
created_reservation_id = Column(Integer, ForeignKey("reservations.id"), nullable=True)
|
||||
|
||||
# Relationships
|
||||
webhook_endpoint = relationship("WebhookEndpoint", back_populates="webhook_requests")
|
||||
hotel = relationship("Hotel")
|
||||
customer = relationship("Customer")
|
||||
reservation = relationship("Reservation")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_webhook_status_created', 'status', 'created_at'),
|
||||
Index('idx_webhook_hotel_created', 'hotel_id', 'created_at'),
|
||||
Index('idx_webhook_purge_candidate', 'status', 'purged_at', 'created_at'),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user