This commit is contained in:
patrick-motsch 2026-02-12 01:40:00 +01:00
parent 7051a6e35f
commit 1ce125ac75
4 changed files with 37 additions and 38 deletions

View file

@ -501,25 +501,25 @@ class AutomationObjects:
System templates (isSystem=True) are always included (read-only for non-SysAdmin). System templates (isSystem=True) are always included (read-only for non-SysAdmin).
Instance templates (featureInstanceId matches) are included with RBAC filtering. Instance templates (featureInstanceId matches) are included with RBAC filtering.
""" """
# 1. System templates — always visible to all users # Load ALL templates and filter in Python.
systemTemplates = self.db.getRecordset( # Reason: seeded/legacy templates may have isSystem=NULL (not False/True),
AutomationTemplate, # which breaks SQL equality filters (NULL != True AND NULL != False).
recordFilter={"isSystem": True} allTemplates = self.db.getRecordset(AutomationTemplate)
)
# 2. Instance templates — scoped to current feature instance, RBAC-filtered filteredTemplates = []
instanceTemplates = [] for t in allTemplates:
if self.featureInstanceId: isSystem = t.get("isSystem")
allInstanceTemplates = self.db.getRecordset( fid = t.get("featureInstanceId")
AutomationTemplate,
recordFilter={"featureInstanceId": self.featureInstanceId, "isSystem": False} if isSystem is True:
) # System templates — always visible to all users
# Apply RBAC filtering on instance templates filteredTemplates.append(t)
for t in allInstanceTemplates: elif fid and fid == self.featureInstanceId:
instanceTemplates.append(t) # Instance templates — scoped to current feature instance
filteredTemplates.append(t)
# Combine: system first, then instance elif not fid:
filteredTemplates = systemTemplates + instanceTemplates # Global/legacy templates (no featureInstanceId) — visible to all users
filteredTemplates.append(t)
# Enrich with user names # Enrich with user names
self._enrichTemplatesWithUserName(filteredTemplates) self._enrichTemplatesWithUserName(filteredTemplates)

View file

@ -166,6 +166,7 @@ def initAutomationTemplates(dbApp: DatabaseConnector, adminUserId: Optional[str]
"label": labelDict, "label": labelDict,
"overview": overviewDict, "overview": overviewDict,
"template": json.dumps(templateContent), # Store entire template JSON "template": json.dumps(templateContent), # Store entire template JSON
"isSystem": True, # Seeded templates are system-level, visible to all users
} }
try: try:

View file

@ -1242,27 +1242,23 @@ class ChatObjects:
if not self.checkRbacPermission(ChatWorkflow, "update", workflowId): if not self.checkRbacPermission(ChatWorkflow, "update", workflowId):
raise PermissionError(f"No permission to modify workflow {workflowId}") raise PermissionError(f"No permission to modify workflow {workflowId}")
# Check if the message exists # Check if the message exists (bypass RBAC -- workflow access already verified above)
messages = self.getMessages(workflowId) matchingMessages = self.db.getRecordset(ChatMessage, recordFilter={"id": messageId, "workflowId": workflowId})
message = next((m for m in messages if m.get("id") == messageId), None)
if not message: if not matchingMessages:
logger.warning(f"Message {messageId} for workflow {workflowId} not found") logger.warning(f"Message {messageId} for workflow {workflowId} not found")
return False return False
# CASCADE DELETE: Delete all related data first # CASCADE DELETE: Delete all related data first
# 1. Delete message stats # 1. Delete message documents (but NOT the files themselves)
existing_stats = self._getRecordset(ChatStat, recordFilter={"messageId": messageId}) # Bypass RBAC -- workflow access already verified, child records may have different _createdBy
for stat in existing_stats: existing_docs = self.db.getRecordset(ChatDocument, recordFilter={"messageId": messageId})
self.db.recordDelete(ChatStat, stat["id"])
# 2. Delete message documents (but NOT the files!)
existing_docs = self._getRecordset(ChatDocument, recordFilter={"messageId": messageId})
for doc in existing_docs: for doc in existing_docs:
self.db.recordDelete(ChatDocument, doc["id"]) self.db.recordDelete(ChatDocument, doc["id"])
# 3. Finally delete the message itself # 2. Finally delete the message itself
# Note: ChatStat has no messageId field -- stats are workflow-level, not message-level
success = self.db.recordDelete(ChatMessage, messageId) success = self.db.recordDelete(ChatMessage, messageId)
return success return success
@ -1285,11 +1281,14 @@ class ChatObjects:
# Get documents for this message from normalized table # Get documents for this message from normalized table
documents = self._getRecordset(ChatDocument, recordFilter={"messageId": messageId}) # Bypass RBAC -- workflow access already verified, child records may have different _createdBy
documents = self.db.getRecordset(ChatDocument, recordFilter={"messageId": messageId})
if not documents: if not documents:
logger.warning(f"No documents found for message {messageId}") # No ChatDocument records -- documents may be stored inline on the message (legacy).
return False # The frontend handles removal optimistically, file itself is preserved.
logger.debug(f"No ChatDocument records for message {messageId} -- inline/legacy data, nothing to delete")
return True
# Find and delete the specific document # Find and delete the specific document
removed = False removed = False
@ -1316,6 +1315,8 @@ class ChatObjects:
if not removed: if not removed:
logger.warning(f"No matching file {fileId} found in message {messageId}") logger.warning(f"No matching file {fileId} found in message {messageId}")
return False return False
return True
except Exception as e: except Exception as e:
logger.error(f"Error removing file {fileId} from message {messageId}: {str(e)}") logger.error(f"Error removing file {fileId} from message {messageId}: {str(e)}")

View file

@ -549,11 +549,8 @@ def delete_workflow_message(
detail=f"Message with ID {messageId} not found in workflow {workflowId}" detail=f"Message with ID {messageId} not found in workflow {workflowId}"
) )
# Update workflow's messageIds # Messages are stored in ChatMessage table linked by workflowId --
messageIds = workflow.get("messageIds", []) # no messageIds list on ChatWorkflow to update.
if messageId in messageIds:
messageIds.remove(messageId)
interfaceDbChat.updateWorkflow(workflowId, {"messageIds": messageIds})
return { return {
"workflowId": workflowId, "workflowId": workflowId,