centralized timestamp management with logger
This commit is contained in:
parent
a225c17841
commit
88110d0f9d
26 changed files with 178 additions and 94 deletions
|
|
@ -7,7 +7,7 @@ from pydantic import BaseModel
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import uuid
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import List, Dict, Any, Optional
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from modules.shared.attributeUtils import registerModelLabels
|
from modules.shared.attributeUtils import registerModelLabels
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
from typing import Dict, Any, Optional, Union
|
from typing import Dict, Any, Optional, Union
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from modules.shared.attributeUtils import registerModelLabels
|
from modules.shared.attributeUtils import registerModelLabels
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
import uuid
|
import uuid
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from modules.shared.attributeUtils import registerModelLabels
|
from modules.shared.attributeUtils import registerModelLabels
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
from .datamodelUam import AuthAuthority
|
from .datamodelUam import AuthAuthority
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import uuid
|
import uuid
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from typing import Optional
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pydantic import BaseModel, Field, EmailStr
|
from pydantic import BaseModel, Field, EmailStr
|
||||||
from modules.shared.attributeUtils import registerModelLabels
|
from modules.shared.attributeUtils import registerModelLabels
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
|
|
||||||
|
|
||||||
class AuthAuthority(str, Enum):
|
class AuthAuthority(str, Enum):
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from modules.shared.attributeUtils import registerModelLabels
|
from modules.shared.attributeUtils import registerModelLabels
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import uuid
|
||||||
|
|
||||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp, parseTimestamp
|
||||||
from modules.interfaces.interfaceDbAppAccess import AppAccess
|
from modules.interfaces.interfaceDbAppAccess import AppAccess
|
||||||
from modules.datamodels.datamodelUam import (
|
from modules.datamodels.datamodelUam import (
|
||||||
User,
|
User,
|
||||||
|
|
@ -1019,7 +1019,7 @@ class AppObjects:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Sort by expiration date and get the latest (most recent expiration)
|
# Sort by expiration date and get the latest (most recent expiration)
|
||||||
tokens.sort(key=lambda x: x.get("expiresAt", 0), reverse=True)
|
tokens.sort(key=lambda x: parseTimestamp(x.get("expiresAt"), default=0), reverse=True)
|
||||||
latest_token = Token(**tokens[0])
|
latest_token = Token(**tokens[0])
|
||||||
|
|
||||||
# No auto-refresh here. Callers should use a higher-level service to refresh when needed.
|
# No auto-refresh here. Callers should use a higher-level service to refresh when needed.
|
||||||
|
|
@ -1170,10 +1170,8 @@ class AppObjects:
|
||||||
all_tokens = self.db.getRecordset(Token, recordFilter={})
|
all_tokens = self.db.getRecordset(Token, recordFilter={})
|
||||||
|
|
||||||
for token_data in all_tokens:
|
for token_data in all_tokens:
|
||||||
if (
|
expiresAt = parseTimestamp(token_data.get("expiresAt"))
|
||||||
token_data.get("expiresAt")
|
if expiresAt and expiresAt < current_time:
|
||||||
and token_data.get("expiresAt") < current_time
|
|
||||||
):
|
|
||||||
# Token is expired, delete it
|
# Token is expired, delete it
|
||||||
self.db.recordDelete(Token, token_data["id"])
|
self.db.recordDelete(Token, token_data["id"])
|
||||||
cleaned_count += 1
|
cleaned_count += 1
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ from modules.datamodels.datamodelUam import User
|
||||||
|
|
||||||
# DYNAMIC PART: Connectors to the Interface
|
# DYNAMIC PART: Connectors to the Interface
|
||||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp, parseTimestamp
|
||||||
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResult
|
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResult
|
||||||
|
|
||||||
# Basic Configurations
|
# Basic Configurations
|
||||||
|
|
@ -995,7 +995,7 @@ class ChatObjects:
|
||||||
|
|
||||||
# Apply default sorting by timestamp if no sort specified
|
# Apply default sorting by timestamp if no sort specified
|
||||||
if pagination is None or not pagination.sort:
|
if pagination is None or not pagination.sort:
|
||||||
logDicts.sort(key=lambda x: float(x.get("timestamp", 0)))
|
logDicts.sort(key=lambda x: parseTimestamp(x.get("timestamp"), default=0))
|
||||||
|
|
||||||
# Apply filtering (if filters provided)
|
# Apply filtering (if filters provided)
|
||||||
if pagination and pagination.filters:
|
if pagination and pagination.filters:
|
||||||
|
|
@ -1143,15 +1143,15 @@ class ChatObjects:
|
||||||
messages = self.db.getRecordset(ChatMessage, recordFilter={"workflowId": workflowId})
|
messages = self.db.getRecordset(ChatMessage, recordFilter={"workflowId": workflowId})
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
# Apply timestamp filtering in Python
|
# Apply timestamp filtering in Python
|
||||||
msg_timestamp = msg.get("publishedAt", getUtcTimestamp())
|
msgTimestamp = parseTimestamp(msg.get("publishedAt"), default=getUtcTimestamp())
|
||||||
if afterTimestamp is not None and msg_timestamp <= afterTimestamp:
|
if afterTimestamp is not None and msgTimestamp <= afterTimestamp:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Load documents for each message
|
# Load documents for each message
|
||||||
documents = self.getDocuments(msg["id"])
|
documents = self.getDocuments(msg["id"])
|
||||||
|
|
||||||
# Create ChatMessage object with loaded documents
|
# Create ChatMessage object with loaded documents
|
||||||
chat_message = ChatMessage(
|
chatMessage = ChatMessage(
|
||||||
id=msg["id"],
|
id=msg["id"],
|
||||||
workflowId=msg["workflowId"],
|
workflowId=msg["workflowId"],
|
||||||
parentMessageId=msg.get("parentMessageId"),
|
parentMessageId=msg.get("parentMessageId"),
|
||||||
|
|
@ -1176,23 +1176,23 @@ class ChatObjects:
|
||||||
# Use publishedAt as the timestamp for chronological ordering
|
# Use publishedAt as the timestamp for chronological ordering
|
||||||
items.append({
|
items.append({
|
||||||
"type": "message",
|
"type": "message",
|
||||||
"createdAt": msg_timestamp,
|
"createdAt": msgTimestamp,
|
||||||
"item": chat_message
|
"item": chatMessage
|
||||||
})
|
})
|
||||||
|
|
||||||
# Get logs
|
# Get logs
|
||||||
logs = self.db.getRecordset(ChatLog, recordFilter={"workflowId": workflowId})
|
logs = self.db.getRecordset(ChatLog, recordFilter={"workflowId": workflowId})
|
||||||
for log in logs:
|
for log in logs:
|
||||||
# Apply timestamp filtering in Python
|
# Apply timestamp filtering in Python
|
||||||
log_timestamp = log.get("timestamp", getUtcTimestamp())
|
logTimestamp = parseTimestamp(log.get("timestamp"), default=getUtcTimestamp())
|
||||||
if afterTimestamp is not None and log_timestamp <= afterTimestamp:
|
if afterTimestamp is not None and logTimestamp <= afterTimestamp:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
chat_log = ChatLog(**log)
|
chatLog = ChatLog(**log)
|
||||||
items.append({
|
items.append({
|
||||||
"type": "log",
|
"type": "log",
|
||||||
"createdAt": log_timestamp,
|
"createdAt": logTimestamp,
|
||||||
"item": chat_log
|
"item": chatLog
|
||||||
})
|
})
|
||||||
|
|
||||||
# Get stats list
|
# Get stats list
|
||||||
|
|
@ -1210,7 +1210,7 @@ class ChatObjects:
|
||||||
})
|
})
|
||||||
|
|
||||||
# Sort all items by createdAt timestamp for chronological order
|
# Sort all items by createdAt timestamp for chronological order
|
||||||
items.sort(key=lambda x: x["createdAt"])
|
items.sort(key=lambda x: parseTimestamp(x.get("createdAt"), default=0))
|
||||||
|
|
||||||
return {"items": items}
|
return {"items": items}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from modules.datamodels.datamodelUtils import Prompt
|
||||||
from modules.datamodels.datamodelVoice import VoiceSettings
|
from modules.datamodels.datamodelVoice import VoiceSettings
|
||||||
from modules.datamodels.datamodelUam import User, Mandate
|
from modules.datamodels.datamodelUam import User, Mandate
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResult
|
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResult
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from typing import Dict, Any, Optional, List
|
||||||
from modules.connectors.connectorVoiceGoogle import ConnectorGoogleSpeech
|
from modules.connectors.connectorVoiceGoogle import ConnectorGoogleSpeech
|
||||||
from modules.datamodels.datamodelVoice import VoiceSettings
|
from modules.datamodels.datamodelVoice import VoiceSettings
|
||||||
from modules.datamodels.datamodelUam import User
|
from modules.datamodels.datamodelUam import User
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from modules.datamodels.datamodelUam import User, UserConnection, AuthAuthority,
|
||||||
from modules.datamodels.datamodelSecurity import Token
|
from modules.datamodels.datamodelSecurity import Token
|
||||||
from modules.security.auth import getCurrentUser, limiter
|
from modules.security.auth import getCurrentUser, limiter
|
||||||
from modules.interfaces.interfaceDbAppObjects import getInterface
|
from modules.interfaces.interfaceDbAppObjects import getInterface
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp, parseTimestamp
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -51,7 +51,7 @@ def getTokenStatusForConnection(interface, connectionId: str) -> tuple[str, Opti
|
||||||
latestCreatedAt = 0
|
latestCreatedAt = 0
|
||||||
|
|
||||||
for tokenData in tokens:
|
for tokenData in tokens:
|
||||||
createdAt = tokenData.get("createdAt", 0)
|
createdAt = parseTimestamp(tokenData.get("createdAt"), default=0)
|
||||||
if createdAt > latestCreatedAt:
|
if createdAt > latestCreatedAt:
|
||||||
latestCreatedAt = createdAt
|
latestCreatedAt = createdAt
|
||||||
latestToken = tokenData
|
latestToken = tokenData
|
||||||
|
|
@ -60,7 +60,7 @@ def getTokenStatusForConnection(interface, connectionId: str) -> tuple[str, Opti
|
||||||
return "none", None
|
return "none", None
|
||||||
|
|
||||||
# Check if token is expired
|
# Check if token is expired
|
||||||
expiresAt = latestToken.get("expiresAt")
|
expiresAt = parseTimestamp(latestToken.get("expiresAt"))
|
||||||
if not expiresAt:
|
if not expiresAt:
|
||||||
return "none", None
|
return "none", None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,13 +126,24 @@ async def get_user(
|
||||||
async def create_user(
|
async def create_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
user_data: User = Body(...),
|
user_data: User = Body(...),
|
||||||
|
password: Optional[str] = Body(None, embed=True),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> User:
|
) -> User:
|
||||||
"""Create a new user"""
|
"""Create a new user"""
|
||||||
appInterface = interfaceDbAppObjects.getInterface(currentUser)
|
appInterface = interfaceDbAppObjects.getInterface(currentUser)
|
||||||
|
|
||||||
# Create user
|
# Extract fields from User model and call createUser with individual parameters
|
||||||
newUser = appInterface.createUser(user_data)
|
from modules.datamodels.datamodelUam import AuthAuthority
|
||||||
|
newUser = appInterface.createUser(
|
||||||
|
username=user_data.username,
|
||||||
|
password=password,
|
||||||
|
email=user_data.email,
|
||||||
|
fullName=user_data.fullName,
|
||||||
|
language=user_data.language,
|
||||||
|
enabled=user_data.enabled,
|
||||||
|
privilege=user_data.privilege,
|
||||||
|
authenticationAuthority=user_data.authenticationAuthority
|
||||||
|
)
|
||||||
|
|
||||||
return newUser
|
return newUser
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from modules.interfaces.interfaceDbAppObjects import getInterface, getRootInterf
|
||||||
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
||||||
from modules.security.auth import getCurrentUser, limiter
|
from modules.security.auth import getCurrentUser, limiter
|
||||||
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
||||||
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
from modules.shared.timeUtils import createExpirationTimestamp, getUtcTimestamp, parseTimestamp
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -264,7 +264,7 @@ async def auth_callback(code: str, state: str, request: Request, response: Respo
|
||||||
})
|
})
|
||||||
if existing_tokens:
|
if existing_tokens:
|
||||||
# Use most recent by createdAt
|
# Use most recent by createdAt
|
||||||
existing_tokens.sort(key=lambda x: x.get("createdAt", 0), reverse=True)
|
existing_tokens.sort(key=lambda x: parseTimestamp(x.get("createdAt"), default=0), reverse=True)
|
||||||
token_response["refresh_token"] = existing_tokens[0].get("tokenRefresh", "")
|
token_response["refresh_token"] = existing_tokens[0].get("tokenRefresh", "")
|
||||||
if not token_response.get("refresh_token") and user_id:
|
if not token_response.get("refresh_token") and user_id:
|
||||||
existing_access_tokens = rootInterface.db.getRecordset(Token, recordFilter={
|
existing_access_tokens = rootInterface.db.getRecordset(Token, recordFilter={
|
||||||
|
|
@ -273,7 +273,7 @@ async def auth_callback(code: str, state: str, request: Request, response: Respo
|
||||||
"authority": AuthAuthority.GOOGLE
|
"authority": AuthAuthority.GOOGLE
|
||||||
})
|
})
|
||||||
if existing_access_tokens:
|
if existing_access_tokens:
|
||||||
existing_access_tokens.sort(key=lambda x: x.get("createdAt", 0), reverse=True)
|
existing_access_tokens.sort(key=lambda x: parseTimestamp(x.get("createdAt"), default=0), reverse=True)
|
||||||
token_response["refresh_token"] = existing_access_tokens[0].get("tokenRefresh", "")
|
token_response["refresh_token"] = existing_access_tokens[0].get("tokenRefresh", "")
|
||||||
except Exception:
|
except Exception:
|
||||||
# Non-fatal; continue without refresh token
|
# Non-fatal; continue without refresh token
|
||||||
|
|
@ -748,19 +748,21 @@ async def refresh_token(
|
||||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to refresh token")
|
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to refresh token")
|
||||||
|
|
||||||
# Update the connection status and timing
|
# Update the connection status and timing
|
||||||
google_connection.expiresAt = float(current_token.expiresAt) if current_token.expiresAt else google_connection.expiresAt
|
expiresAtValue = parseTimestamp(current_token.expiresAt)
|
||||||
|
google_connection.expiresAt = expiresAtValue if expiresAtValue else google_connection.expiresAt
|
||||||
google_connection.lastChecked = getUtcTimestamp()
|
google_connection.lastChecked = getUtcTimestamp()
|
||||||
google_connection.status = ConnectionStatus.ACTIVE
|
google_connection.status = ConnectionStatus.ACTIVE
|
||||||
appInterface.db.recordModify(UserConnection, google_connection.id, google_connection.model_dump())
|
appInterface.db.recordModify(UserConnection, google_connection.id, google_connection.model_dump())
|
||||||
|
|
||||||
# Calculate time until expiration
|
# Calculate time until expiration
|
||||||
current_time = getUtcTimestamp()
|
currentTime = getUtcTimestamp()
|
||||||
expires_in = int(current_token.expiresAt - current_time) if current_token.expiresAt else 0
|
expiresAt = parseTimestamp(current_token.expiresAt)
|
||||||
|
expiresIn = int(expiresAt - currentTime) if expiresAt else 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"message": "Token refreshed successfully",
|
"message": "Token refreshed successfully",
|
||||||
"expires_at": current_token.expiresAt,
|
"expires_at": expiresAt,
|
||||||
"expires_in_seconds": expires_in
|
"expires_in_seconds": expiresIn
|
||||||
}
|
}
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatu
|
||||||
from modules.datamodels.datamodelSecurity import Token
|
from modules.datamodels.datamodelSecurity import Token
|
||||||
from modules.security.auth import getCurrentUser, limiter
|
from modules.security.auth import getCurrentUser, limiter
|
||||||
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
||||||
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
from modules.shared.timeUtils import createExpirationTimestamp, getUtcTimestamp, parseTimestamp
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -639,7 +639,9 @@ async def refresh_token(
|
||||||
appInterface.saveConnectionToken(refreshedToken)
|
appInterface.saveConnectionToken(refreshedToken)
|
||||||
|
|
||||||
# Update the connection's expiration time
|
# Update the connection's expiration time
|
||||||
msft_connection.expiresAt = float(refreshedToken.expiresAt)
|
expiresAtValue = parseTimestamp(refreshedToken.expiresAt)
|
||||||
|
if expiresAtValue:
|
||||||
|
msft_connection.expiresAt = expiresAtValue
|
||||||
msft_connection.lastChecked = getUtcTimestamp()
|
msft_connection.lastChecked = getUtcTimestamp()
|
||||||
msft_connection.status = ConnectionStatus.ACTIVE
|
msft_connection.status = ConnectionStatus.ACTIVE
|
||||||
|
|
||||||
|
|
@ -647,12 +649,13 @@ async def refresh_token(
|
||||||
appInterface.db.recordModify(UserConnection, msft_connection.id, msft_connection.model_dump())
|
appInterface.db.recordModify(UserConnection, msft_connection.id, msft_connection.model_dump())
|
||||||
|
|
||||||
# Calculate time until expiration
|
# Calculate time until expiration
|
||||||
current_time = getUtcTimestamp()
|
currentTime = getUtcTimestamp()
|
||||||
expiresIn = int(refreshedToken.expiresAt - current_time)
|
expiresAt = parseTimestamp(refreshedToken.expiresAt)
|
||||||
|
expiresIn = int(expiresAt - currentTime) if expiresAt else 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"message": "Token refreshed successfully",
|
"message": "Token refreshed successfully",
|
||||||
"expires_at": refreshedToken.expiresAt,
|
"expires_at": expiresAt,
|
||||||
"expires_in_seconds": expiresIn
|
"expires_in_seconds": expiresIn
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from fastapi import Response
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
|
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.shared.timezoneUtils import getUtcNow
|
from modules.shared.timeUtils import getUtcNow
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
SECRET_KEY = APP_CONFIG.get("APP_JWT_KEY_SECRET")
|
SECRET_KEY = APP_CONFIG.get("APP_JWT_KEY_SECRET")
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from typing import Optional, Dict, Any, Callable
|
||||||
from modules.datamodels.datamodelSecurity import Token
|
from modules.datamodels.datamodelSecurity import Token
|
||||||
from modules.datamodels.datamodelUam import AuthAuthority
|
from modules.datamodels.datamodelUam import AuthAuthority
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp, createExpirationTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp, createExpirationTimestamp, parseTimestamp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@ class TokenManager:
|
||||||
# Only allow a new refresh if at least 10 minutes passed since the token was created/refreshed
|
# Only allow a new refresh if at least 10 minutes passed since the token was created/refreshed
|
||||||
try:
|
try:
|
||||||
nowTs = getUtcTimestamp()
|
nowTs = getUtcTimestamp()
|
||||||
createdTs = float(oldToken.createdAt) if oldToken.createdAt is not None else 0.0
|
createdTs = parseTimestamp(oldToken.createdAt, default=0.0)
|
||||||
secondsSinceLastRefresh = nowTs - createdTs
|
secondsSinceLastRefresh = nowTs - createdTs
|
||||||
if secondsSinceLastRefresh < 10 * 60:
|
if secondsSinceLastRefresh < 10 * 60:
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
import asyncio
|
import asyncio
|
||||||
from modules.security.tokenRefreshService import token_refresh_service
|
from modules.security.tokenRefreshService import token_refresh_service
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ to ensure users don't experience token expiration issues.
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
from modules.datamodels.datamodelUam import UserConnection, AuthAuthority
|
from modules.datamodels.datamodelUam import UserConnection, AuthAuthority
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
from modules.shared.auditLogger import audit_logger
|
from modules.shared.auditLogger import audit_logger
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import logging
|
||||||
from typing import Any, Optional, Dict, Callable, List
|
from typing import Any, Optional, Dict, Callable, List
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.shared.eventManagement import eventManager
|
from modules.shared.eventManagement import eventManager
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
from modules.shared import jsonUtils
|
from modules.shared import jsonUtils
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ def debugLogToFile(message: str, context: str = "DEBUG") -> None:
|
||||||
debug_file = os.path.join(debug_dir, "debug_workflow.log")
|
debug_file = os.path.join(debug_dir, "debug_workflow.log")
|
||||||
|
|
||||||
# Format the debug entry
|
# Format the debug entry
|
||||||
from modules.shared.timezoneUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp
|
||||||
timestamp = getUtcTimestamp()
|
timestamp = getUtcTimestamp()
|
||||||
debug_entry = f"[{timestamp}] [{context}] {message}\n"
|
debug_entry = f"[{timestamp}] [{context}] {message}\n"
|
||||||
|
|
||||||
|
|
|
||||||
104
modules/shared/timeUtils.py
Normal file
104
modules/shared/timeUtils.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"""
|
||||||
|
Timezone utilities for consistent timestamp handling across the gateway.
|
||||||
|
Ensures all timestamps are properly handled as UTC.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Optional, Any
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Configure logger
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def getUtcNow() -> datetime:
|
||||||
|
"""
|
||||||
|
Get current time in UTC with timezone info.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
datetime: Current UTC time with timezone info
|
||||||
|
"""
|
||||||
|
return datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
def getUtcTimestamp() -> float:
|
||||||
|
"""
|
||||||
|
Get current UTC timestamp (seconds since epoch with millisecond precision).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Current UTC timestamp in seconds with millisecond precision
|
||||||
|
"""
|
||||||
|
return time.time()
|
||||||
|
|
||||||
|
def createExpirationTimestamp(expiresInSeconds: int) -> float:
|
||||||
|
"""
|
||||||
|
Create a new expiration timestamp from seconds until expiration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
expiresInSeconds (int): Seconds until expiration
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: UTC timestamp in seconds
|
||||||
|
"""
|
||||||
|
return getUtcTimestamp() + expiresInSeconds
|
||||||
|
|
||||||
|
def parseTimestamp(value: Any, default: Optional[float] = None) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Parse a timestamp value from various source formats and return as float.
|
||||||
|
|
||||||
|
Handles conversion from:
|
||||||
|
- float: Returns as-is
|
||||||
|
- int: Converts to float
|
||||||
|
- str: Attempts to parse as numeric string (e.g., "1234567890.123")
|
||||||
|
- None: Returns default value (or None if no default)
|
||||||
|
|
||||||
|
This function is useful when database connectors (e.g., psycopg2) may return
|
||||||
|
numeric fields as strings in some environments (e.g., Azure PostgreSQL).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The timestamp value to parse (can be float, int, str, or None)
|
||||||
|
default: Optional default value to return if value is None or invalid.
|
||||||
|
If None and value is invalid, returns None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Parsed timestamp as float, or default value if provided, or None
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> parseTimestamp(1234567890.123)
|
||||||
|
1234567890.123
|
||||||
|
>>> parseTimestamp("1234567890.123")
|
||||||
|
1234567890.123
|
||||||
|
>>> parseTimestamp(None, default=0.0)
|
||||||
|
0.0
|
||||||
|
>>> parseTimestamp("invalid", default=getUtcTimestamp())
|
||||||
|
# Returns current timestamp
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
|
||||||
|
# Already a float
|
||||||
|
if isinstance(value, float):
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Integer - convert to float
|
||||||
|
if isinstance(value, int):
|
||||||
|
return float(value)
|
||||||
|
|
||||||
|
# String - try to parse as numeric
|
||||||
|
if isinstance(value, str):
|
||||||
|
# Empty string
|
||||||
|
if not value.strip():
|
||||||
|
logger.warning(f"parseTimestamp: Received empty string, returning default={default}")
|
||||||
|
return default
|
||||||
|
try:
|
||||||
|
return float(value)
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
# Invalid string format
|
||||||
|
logger.warning(f"parseTimestamp: Failed to parse string '{value}' as float: {type(e).__name__}: {str(e)}, returning default={default}")
|
||||||
|
return default
|
||||||
|
|
||||||
|
# Unknown type - try to convert anyway
|
||||||
|
try:
|
||||||
|
return float(value)
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
logger.warning(f"parseTimestamp: Failed to convert value of type {type(value).__name__} '{value}' to float: {type(e).__name__}: {str(e)}, returning default={default}")
|
||||||
|
return default
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
"""
|
|
||||||
Timezone utilities for consistent timestamp handling across the gateway.
|
|
||||||
Ensures all timestamps are properly handled as UTC.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
import time
|
|
||||||
|
|
||||||
def getUtcNow() -> datetime:
|
|
||||||
"""
|
|
||||||
Get current time in UTC with timezone info.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
datetime: Current UTC time with timezone info
|
|
||||||
"""
|
|
||||||
return datetime.now(timezone.utc)
|
|
||||||
|
|
||||||
def getUtcTimestamp() -> float:
|
|
||||||
"""
|
|
||||||
Get current UTC timestamp (seconds since epoch with millisecond precision).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: Current UTC timestamp in seconds with millisecond precision
|
|
||||||
"""
|
|
||||||
return time.time()
|
|
||||||
|
|
||||||
def createExpirationTimestamp(expiresInSeconds: int) -> float:
|
|
||||||
"""
|
|
||||||
Create a new expiration timestamp from seconds until expiration.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
expiresInSeconds (int): Seconds until expiration
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: UTC timestamp in seconds
|
|
||||||
"""
|
|
||||||
return getUtcTimestamp() + expiresInSeconds
|
|
||||||
|
|
@ -14,6 +14,7 @@ from modules.datamodels.datamodelChat import ChatWorkflow
|
||||||
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, ProcessingModeEnum, PriorityEnum
|
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, ProcessingModeEnum, PriorityEnum
|
||||||
from modules.workflows.processing.modes.modeBase import BaseMode
|
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||||
|
from modules.shared.timeUtils import parseTimestamp
|
||||||
from modules.workflows.processing.shared.executionState import TaskExecutionState
|
from modules.workflows.processing.shared.executionState import TaskExecutionState
|
||||||
from modules.workflows.processing.shared.promptGenerationActionsActionplan import (
|
from modules.workflows.processing.shared.promptGenerationActionsActionplan import (
|
||||||
generateActionDefinitionPrompt,
|
generateActionDefinitionPrompt,
|
||||||
|
|
@ -662,7 +663,7 @@ class ActionplanMode(BaseMode):
|
||||||
retryCount=createdAction.get("retryCount", 0),
|
retryCount=createdAction.get("retryCount", 0),
|
||||||
retryMax=createdAction.get("retryMax", 3),
|
retryMax=createdAction.get("retryMax", 3),
|
||||||
processingTime=createdAction.get("processingTime"),
|
processingTime=createdAction.get("processingTime"),
|
||||||
timestamp=float(createdAction.get("timestamp", self.services.utils.timestampGetUtc())),
|
timestamp=parseTimestamp(createdAction.get("timestamp"), default=self.services.utils.timestampGetUtc()),
|
||||||
result=createdAction.get("result"),
|
result=createdAction.get("result"),
|
||||||
resultDocuments=createdAction.get("resultDocuments", []),
|
resultDocuments=createdAction.get("resultDocuments", []),
|
||||||
userMessage=createdAction.get("userMessage")
|
userMessage=createdAction.get("userMessage")
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from modules.datamodels.datamodelChat import (
|
||||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||||
from modules.workflows.processing.modes.modeBase import BaseMode
|
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||||
|
from modules.shared.timeUtils import parseTimestamp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -342,7 +343,7 @@ class AutomationMode(BaseMode):
|
||||||
retryCount=createdAction.get("retryCount", 0),
|
retryCount=createdAction.get("retryCount", 0),
|
||||||
retryMax=createdAction.get("retryMax", 3),
|
retryMax=createdAction.get("retryMax", 3),
|
||||||
processingTime=createdAction.get("processingTime"),
|
processingTime=createdAction.get("processingTime"),
|
||||||
timestamp=float(createdAction.get("timestamp", self.services.utils.timestampGetUtc())),
|
timestamp=parseTimestamp(createdAction.get("timestamp"), default=self.services.utils.timestampGetUtc()),
|
||||||
result=createdAction.get("result"),
|
result=createdAction.get("result"),
|
||||||
userMessage=createdAction.get("userMessage")
|
userMessage=createdAction.get("userMessage")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from modules.datamodels.datamodelChat import (
|
||||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||||
from modules.workflows.processing.modes.modeBase import BaseMode
|
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||||
|
from modules.shared.timeUtils import parseTimestamp
|
||||||
from modules.workflows.processing.shared.executionState import TaskExecutionState, shouldContinue
|
from modules.workflows.processing.shared.executionState import TaskExecutionState, shouldContinue
|
||||||
from modules.workflows.processing.shared.promptGenerationActionsDynamic import (
|
from modules.workflows.processing.shared.promptGenerationActionsDynamic import (
|
||||||
generateDynamicPlanSelectionPrompt,
|
generateDynamicPlanSelectionPrompt,
|
||||||
|
|
@ -867,7 +868,7 @@ Return only the user-friendly message, no technical details."""
|
||||||
retryCount=createdAction.get("retryCount", 0),
|
retryCount=createdAction.get("retryCount", 0),
|
||||||
retryMax=createdAction.get("retryMax", 3),
|
retryMax=createdAction.get("retryMax", 3),
|
||||||
processingTime=createdAction.get("processingTime"),
|
processingTime=createdAction.get("processingTime"),
|
||||||
timestamp=float(createdAction.get("timestamp", self.services.utils.timestampGetUtc())),
|
timestamp=parseTimestamp(createdAction.get("timestamp"), default=self.services.utils.timestampGetUtc()),
|
||||||
result=createdAction.get("result"),
|
result=createdAction.get("result"),
|
||||||
resultDocuments=createdAction.get("resultDocuments", []),
|
resultDocuments=createdAction.get("resultDocuments", []),
|
||||||
userMessage=createdAction.get("userMessage")
|
userMessage=createdAction.get("userMessage")
|
||||||
|
|
@ -960,7 +961,7 @@ Return only the user-friendly message, no technical details."""
|
||||||
retryCount=createdAction.get("retryCount", 0),
|
retryCount=createdAction.get("retryCount", 0),
|
||||||
retryMax=createdAction.get("retryMax", 3),
|
retryMax=createdAction.get("retryMax", 3),
|
||||||
processingTime=createdAction.get("processingTime"),
|
processingTime=createdAction.get("processingTime"),
|
||||||
timestamp=float(createdAction.get("timestamp", self.services.utils.timestampGetUtc())),
|
timestamp=parseTimestamp(createdAction.get("timestamp"), default=self.services.utils.timestampGetUtc()),
|
||||||
result=createdAction.get("result"),
|
result=createdAction.get("result"),
|
||||||
resultDocuments=createdAction.get("resultDocuments", []),
|
resultDocuments=createdAction.get("resultDocuments", []),
|
||||||
userMessage=createdAction.get("userMessage")
|
userMessage=createdAction.get("userMessage")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue