feat: add created_by and last_updated_by user tracking to Entity (#602)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jope-bm
2026-02-23 11:20:59 -07:00
committed by GitHub
parent 0f3889fdd0
commit da4d369c32
6 changed files with 279 additions and 0 deletions
@@ -1,5 +1,6 @@
"""Service for managing entities in the database."""
from collections.abc import Callable
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Sequence, Tuple, Union
@@ -68,6 +69,9 @@ class EntityService(BaseService[EntityModel]):
self.search_service = search_service
self.app_config = app_config
self._project_permalink: Optional[str] = None
# Callable that returns the current user ID (cloud user_profile_id UUID as string).
# Default returns None for local/CLI usage. Cloud overrides this to read from UserContext.
self.get_user_id: Callable[[], Optional[str]] = lambda: None
async def detect_file_path_conflicts(
self, file_path: str, skip_check: bool = False
@@ -445,7 +449,12 @@ class EntityService(BaseService[EntityModel]):
"updated_at": datetime.now().astimezone(),
}
user_id = self.get_user_id()
if existing:
# Preserve existing created_by; only update last_updated_by
if user_id is not None:
update_data["last_updated_by"] = user_id
updated = await self.repository.update(existing.id, update_data)
if not updated:
raise ValueError(f"Failed to update entity in database: {existing.id}")
@@ -454,6 +463,9 @@ class EntityService(BaseService[EntityModel]):
create_data = dict(update_data)
if external_id is not None:
create_data["external_id"] = external_id
if user_id is not None:
create_data["created_by"] = user_id
create_data["last_updated_by"] = user_id
return await self.repository.create(create_data)
async def fast_edit_entity(
@@ -481,6 +493,10 @@ class EntityService(BaseService[EntityModel]):
"checksum": checksum,
"updated_at": datetime.now().astimezone(),
}
user_id = self.get_user_id()
if user_id is not None:
update_data["last_updated_by"] = user_id
content_markdown = None
if has_frontmatter(new_content):
content_frontmatter = parse_frontmatter(new_content)
@@ -611,6 +627,12 @@ class EntityService(BaseService[EntityModel]):
# Mark as incomplete because we still need to add relations
model.checksum = None
# Set user tracking fields for cloud usage
user_id = self.get_user_id()
if user_id is not None:
model.created_by = user_id
model.last_updated_by = user_id
# Use UPSERT to handle conflicts cleanly
try:
return await self.repository.upsert_entity(model)
@@ -653,6 +675,11 @@ class EntityService(BaseService[EntityModel]):
# checksum value is None == not finished with sync
db_entity.checksum = None
# Set last_updated_by for cloud usage (preserve existing created_by)
user_id = self.get_user_id()
if user_id is not None:
db_entity.last_updated_by = user_id
# update entity
return await self.repository.update(
db_entity.id,