diff --git a/services/auth/app/main.py b/services/auth/app/main.py index e58efd3c..0c8026a0 100644 --- a/services/auth/app/main.py +++ b/services/auth/app/main.py @@ -54,13 +54,36 @@ async def lifespan(app: FastAPI): logger.info("Messaging setup complete") # Register custom metrics (metrics_collector already exists) - metrics_collector.register_counter("registration_total", "Total user registrations") - metrics_collector.register_counter("login_success_total", "Successful logins") - metrics_collector.register_counter("login_failure_total", "Failed logins") - metrics_collector.register_counter("token_refresh_total", "Token refresh requests") - metrics_collector.register_counter("token_verify_total", "Token verification requests") - metrics_collector.register_counter("logout_total", "User logout requests") - metrics_collector.register_counter("errors_total", "Total errors") + metrics_collector.register_counter( + "registration_total", + "Total user registrations by status", + labels=["status"] # Add this line + ) + metrics_collector.register_counter( + "login_success_total", + "Total successful user logins" + ) + metrics_collector.register_counter( + "login_failure_total", + "Total failed user logins by reason", + labels=["reason"] # Add this line, based on auth.py usage + ) + metrics_collector.register_counter( + "token_refresh_total", + "Total token refreshes by status", + labels=["status"] # Add this line + ) + metrics_collector.register_counter( + "token_verify_total", + "Total token verifications by status", + labels=["status"] # Add this line + ) + metrics_collector.register_counter( + "logout_total", + "Total user logouts by status", + labels=["status"] # Add this line + ) + metrics_collector.register_counter("errors_total", "Total errors", labels=["type"]) # Add this line metrics_collector.register_histogram("registration_duration_seconds", "Registration request duration") metrics_collector.register_histogram("login_duration_seconds", "Login request duration") metrics_collector.register_histogram("token_refresh_duration_seconds", "Token refresh duration") diff --git a/services/auth/app/models/users.py b/services/auth/app/models/users.py index b966128a..97f0da36 100644 --- a/services/auth/app/models/users.py +++ b/services/auth/app/models/users.py @@ -1,16 +1,19 @@ +# ================================================================ +# services/auth/app/models/users.py - FIXED VERSION +# ================================================================ """ -User models for authentication service +User models for authentication service - FIXED """ from sqlalchemy import Column, String, Boolean, DateTime, Text from sqlalchemy.dialects.postgresql import UUID -from datetime import datetime +from datetime import datetime, timezone import uuid from shared.database.base import Base class User(Base): - """User model""" + """User model - FIXED timezone handling""" __tablename__ = "users" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) @@ -22,9 +25,10 @@ class User(Base): tenant_id = Column(UUID(as_uuid=True), nullable=True) role = Column(String(50), default="user") # user, admin, super_admin - created_at = Column(DateTime, default=datetime.utcnow) - updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - last_login = Column(DateTime) + # FIXED: Use timezone-aware datetime for all datetime fields + created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) + updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) + last_login = Column(DateTime(timezone=True)) # FIXED: Now timezone-aware # Profile fields phone = Column(String(20)) @@ -53,22 +57,23 @@ class User(Base): class UserSession(Base): - """User session model""" + """User session model - FIXED timezone handling""" __tablename__ = "user_sessions" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), nullable=False, index=True) refresh_token_hash = Column(String(255), nullable=False) is_active = Column(Boolean, default=True) - expires_at = Column(DateTime, nullable=False) + expires_at = Column(DateTime(timezone=True), nullable=False) # FIXED: timezone-aware # Session metadata ip_address = Column(String(45)) user_agent = Column(Text) device_info = Column(Text) - created_at = Column(DateTime, default=datetime.utcnow) - updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + # FIXED: Use timezone-aware datetime + created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) + updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) def __repr__(self): return f"" \ No newline at end of file diff --git a/services/auth/app/services/auth_service.py b/services/auth/app/services/auth_service.py index c597d2ab..ce77b937 100644 --- a/services/auth/app/services/auth_service.py +++ b/services/auth/app/services/auth_service.py @@ -12,6 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update from fastapi import HTTPException, status +from app.core.config import settings from app.models.users import User, UserSession from app.schemas.auth import UserRegistration, UserLogin, TokenResponse, UserResponse from app.core.security import security_manager @@ -63,7 +64,6 @@ class AuthService: "user.registered", UserRegisteredEvent( service_name="auth-service", - timestamp=datetime.now(timezone.utc), data={ "user_id": str(user.id), "email": user.email, @@ -149,9 +149,7 @@ class AuthService: "user_events", "user.login", UserLoginEvent( - event_id=str(session.id), service_name="auth-service", - timestamp=datetime.now(timezone.utc), data={ "user_id": str(user.id), "email": user.email, diff --git a/shared/messaging/events.py b/shared/messaging/events.py index ffd88a26..7149bcec 100644 --- a/shared/messaging/events.py +++ b/shared/messaging/events.py @@ -3,7 +3,7 @@ Event definitions for microservices communication - Simple class-based approach to avoid dataclass issues """ -from datetime import datetime +from datetime import datetime, timezone from typing import Dict, Any, Optional import uuid @@ -14,8 +14,19 @@ class BaseEvent: self.data = data self.event_type = event_type self.event_id = str(uuid.uuid4()) - self.timestamp = datetime.utcnow() + self.timestamp = datetime.now(timezone.utc) self.correlation_id = correlation_id + + def to_dict(self) -> Dict[str, Any]: # Add this method + """Converts the event object to a dictionary for JSON serialization.""" + return { + "service_name": self.service_name, + "data": self.data, + "event_type": self.event_type, + "event_id": self.event_id, + "timestamp": self.timestamp.isoformat(), # Convert datetime to ISO 8601 string + "correlation_id": self.correlation_id + } # Training Events class TrainingStartedEvent(BaseEvent):