semantics: complete DEF-to-region migration, fix regressions
- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
# #region StoragePlugin [TYPE Module] [SEMANTICS storage, files, filesystem, plugin]
|
||||
#
|
||||
# @BRIEF Provides core filesystem operations for managing backups and repositories.
|
||||
# @LAYER App
|
||||
# @INVARIANT All file operations must be restricted to the configured storage root.
|
||||
# @RELATION IMPLEMENTS -> [PluginBase]
|
||||
# @RELATION DEPENDS_ON -> [backend.src.models.storage]
|
||||
# @RELATION USES -> [TaskContext]
|
||||
#
|
||||
# @LAYER: App
|
||||
# @RELATION IMPLEMENTS -> PluginBase
|
||||
# @RELATION DEPENDS_ON -> backend.src.models.storage
|
||||
# @RELATION USES -> TaskContext
|
||||
#
|
||||
# @INVARIANT: All file operations must be restricted to the configured storage root.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
@@ -17,14 +16,10 @@ from typing import Dict, Any, List, Optional
|
||||
from fastapi import UploadFile
|
||||
|
||||
from ...core.plugin_base import PluginBase
|
||||
from ...core.logger import belief_scope
|
||||
from ...core.cot_logger import MarkerLogger
|
||||
from ...core.logger import belief_scope, logger
|
||||
from ...models.storage import StoredFile, FileCategory
|
||||
|
||||
log = MarkerLogger("StoragePlugin")
|
||||
from ...dependencies import get_config_manager
|
||||
from ...core.task_manager.context import TaskContext
|
||||
# [/SECTION]
|
||||
|
||||
# #region StoragePlugin [TYPE Class]
|
||||
# @BRIEF Implementation of the storage management plugin.
|
||||
@@ -33,73 +28,73 @@ class StoragePlugin(PluginBase):
|
||||
Plugin for managing local file storage for backups and repositories.
|
||||
"""
|
||||
|
||||
# #region __init__ [TYPE Function]
|
||||
# @BRIEF Initializes the StoragePlugin and ensures required directories exist.
|
||||
# @PRE Configuration manager must be accessible.
|
||||
# @POST Storage root and category directories are created on disk.
|
||||
# [DEF:__init__:Function]
|
||||
# @PURPOSE: Initializes the StoragePlugin and ensures required directories exist.
|
||||
# @PRE: Configuration manager must be accessible.
|
||||
# @POST: Storage root and category directories are created on disk.
|
||||
def __init__(self):
|
||||
with belief_scope("StoragePlugin:init"):
|
||||
self.ensure_directories()
|
||||
# #endregion __init__
|
||||
# [/DEF:__init__:Function]
|
||||
|
||||
@property
|
||||
# #region id [TYPE Function]
|
||||
# @BRIEF Returns the unique identifier for the storage plugin.
|
||||
# @PRE None.
|
||||
# @POST Returns the plugin ID string.
|
||||
# @RETURN str - "storage-manager"
|
||||
# [DEF:id:Function]
|
||||
# @PURPOSE: Returns the unique identifier for the storage plugin.
|
||||
# @PRE: None.
|
||||
# @POST: Returns the plugin ID string.
|
||||
# @RETURN: str - "storage-manager"
|
||||
def id(self) -> str:
|
||||
with belief_scope("StoragePlugin:id"):
|
||||
return "storage-manager"
|
||||
# #endregion id
|
||||
# [/DEF:id:Function]
|
||||
|
||||
@property
|
||||
# #region name [TYPE Function]
|
||||
# @BRIEF Returns the human-readable name of the storage plugin.
|
||||
# @PRE None.
|
||||
# @POST Returns the plugin name string.
|
||||
# @RETURN str - "Storage Manager"
|
||||
# [DEF:name:Function]
|
||||
# @PURPOSE: Returns the human-readable name of the storage plugin.
|
||||
# @PRE: None.
|
||||
# @POST: Returns the plugin name string.
|
||||
# @RETURN: str - "Storage Manager"
|
||||
def name(self) -> str:
|
||||
with belief_scope("StoragePlugin:name"):
|
||||
return "Storage Manager"
|
||||
# #endregion name
|
||||
# [/DEF:name:Function]
|
||||
|
||||
@property
|
||||
# #region description [TYPE Function]
|
||||
# @BRIEF Returns a description of the storage plugin.
|
||||
# @PRE None.
|
||||
# @POST Returns the plugin description string.
|
||||
# @RETURN str - Plugin description.
|
||||
# [DEF:description:Function]
|
||||
# @PURPOSE: Returns a description of the storage plugin.
|
||||
# @PRE: None.
|
||||
# @POST: Returns the plugin description string.
|
||||
# @RETURN: str - Plugin description.
|
||||
def description(self) -> str:
|
||||
with belief_scope("StoragePlugin:description"):
|
||||
return "Manages local file storage for backups and repositories."
|
||||
# #endregion description
|
||||
# [/DEF:description:Function]
|
||||
|
||||
@property
|
||||
# #region version [TYPE Function]
|
||||
# @BRIEF Returns the version of the storage plugin.
|
||||
# @PRE None.
|
||||
# @POST Returns the version string.
|
||||
# @RETURN str - "1.0.0"
|
||||
# [DEF:version:Function]
|
||||
# @PURPOSE: Returns the version of the storage plugin.
|
||||
# @PRE: None.
|
||||
# @POST: Returns the version string.
|
||||
# @RETURN: str - "1.0.0"
|
||||
def version(self) -> str:
|
||||
with belief_scope("StoragePlugin:version"):
|
||||
return "1.0.0"
|
||||
# #endregion version
|
||||
# [/DEF:version:Function]
|
||||
|
||||
@property
|
||||
# #region ui_route [TYPE Function]
|
||||
# @BRIEF Returns the frontend route for the storage plugin.
|
||||
# @RETURN str - "/tools/storage"
|
||||
# [DEF:ui_route:Function]
|
||||
# @PURPOSE: Returns the frontend route for the storage plugin.
|
||||
# @RETURN: str - "/tools/storage"
|
||||
def ui_route(self) -> str:
|
||||
with belief_scope("StoragePlugin:ui_route"):
|
||||
return "/tools/storage"
|
||||
# #endregion ui_route
|
||||
# [/DEF:ui_route:Function]
|
||||
|
||||
# #region get_schema [TYPE Function]
|
||||
# @BRIEF Returns the JSON schema for storage plugin parameters.
|
||||
# @PRE None.
|
||||
# @POST Returns a dictionary representing the JSON schema.
|
||||
# @RETURN Dict[str, Any] - JSON schema.
|
||||
# [DEF:get_schema:Function]
|
||||
# @PURPOSE: Returns the JSON schema for storage plugin parameters.
|
||||
# @PRE: None.
|
||||
# @POST: Returns a dictionary representing the JSON schema.
|
||||
# @RETURN: Dict[str, Any] - JSON schema.
|
||||
def get_schema(self) -> Dict[str, Any]:
|
||||
with belief_scope("StoragePlugin:get_schema"):
|
||||
return {
|
||||
@@ -113,23 +108,30 @@ class StoragePlugin(PluginBase):
|
||||
},
|
||||
"required": ["category"]
|
||||
}
|
||||
# #endregion get_schema
|
||||
# [/DEF:get_schema:Function]
|
||||
|
||||
# #region execute [TYPE Function]
|
||||
# @BRIEF Executes storage-related tasks with TaskContext support.
|
||||
# [DEF:execute:Function]
|
||||
# @PURPOSE: Executes storage-related tasks with TaskContext support.
|
||||
# @PARAM: params (Dict[str, Any]) - Storage parameters.
|
||||
# @PARAM: context (Optional[TaskContext]) - Task context for logging with source attribution.
|
||||
# @PRE: params must match the plugin schema.
|
||||
# @POST: Task is executed and logged.
|
||||
async def execute(self, params: Dict[str, Any], context: Optional[TaskContext] = None):
|
||||
with belief_scope("StoragePlugin:execute"):
|
||||
log.reason(f"Executing with params: {params}")
|
||||
# #endregion execute
|
||||
# Use TaskContext logger if available, otherwise fall back to app logger
|
||||
log = context.logger if context else logger
|
||||
|
||||
# Create sub-loggers for different components
|
||||
storage_log = log.with_source("storage") if context else log
|
||||
log.with_source("filesystem") if context else log
|
||||
|
||||
storage_log.info(f"Executing with params: {params}")
|
||||
# [/DEF:execute:Function]
|
||||
|
||||
# #region get_storage_root [TYPE Function]
|
||||
# @BRIEF Resolves the absolute path to the storage root.
|
||||
# @PRE Settings must define a storage root path.
|
||||
# @POST Returns a Path object representing the storage root.
|
||||
# [DEF:get_storage_root:Function]
|
||||
# @PURPOSE: Resolves the absolute path to the storage root.
|
||||
# @PRE: Settings must define a storage root path.
|
||||
# @POST: Returns a Path object representing the storage root.
|
||||
def get_storage_root(self) -> Path:
|
||||
with belief_scope("StoragePlugin:get_storage_root"):
|
||||
config_manager = get_config_manager()
|
||||
@@ -146,10 +148,10 @@ class StoragePlugin(PluginBase):
|
||||
project_root = Path(__file__).parents[3]
|
||||
root = (project_root / root).resolve()
|
||||
return root
|
||||
# #endregion get_storage_root
|
||||
# [/DEF:get_storage_root:Function]
|
||||
|
||||
# #region resolve_path [TYPE Function]
|
||||
# @BRIEF Resolves a dynamic path pattern using provided variables.
|
||||
# [DEF:resolve_path:Function]
|
||||
# @PURPOSE: Resolves a dynamic path pattern using provided variables.
|
||||
# @PARAM: pattern (str) - The path pattern to resolve.
|
||||
# @PARAM: variables (Dict[str, str]) - Variables to substitute in the pattern.
|
||||
# @PRE: pattern must be a valid format string.
|
||||
@@ -167,16 +169,16 @@ class StoragePlugin(PluginBase):
|
||||
# Clean up any double slashes or leading/trailing slashes for relative path
|
||||
return os.path.normpath(resolved).strip("/")
|
||||
except KeyError as e:
|
||||
log.explore("Missing variable for path resolution", error=str(e))
|
||||
logger.warning(f"[StoragePlugin][Coherence:Failed] Missing variable for path resolution: {e}")
|
||||
# Fallback to literal pattern if formatting fails partially (or handle as needed)
|
||||
return pattern.replace("{", "").replace("}", "")
|
||||
# #endregion resolve_path
|
||||
# [/DEF:resolve_path:Function]
|
||||
|
||||
# #region ensure_directories [TYPE Function]
|
||||
# @BRIEF Creates the storage root and category subdirectories if they don't exist.
|
||||
# @PRE Storage root must be resolvable.
|
||||
# @POST Directories are created on the filesystem.
|
||||
# @SIDE_EFFECT Creates directories on the filesystem.
|
||||
# [DEF:ensure_directories:Function]
|
||||
# @PURPOSE: Creates the storage root and category subdirectories if they don't exist.
|
||||
# @PRE: Storage root must be resolvable.
|
||||
# @POST: Directories are created on the filesystem.
|
||||
# @SIDE_EFFECT: Creates directories on the filesystem.
|
||||
def ensure_directories(self):
|
||||
with belief_scope("StoragePlugin:ensure_directories"):
|
||||
root = self.get_storage_root()
|
||||
@@ -184,13 +186,13 @@ class StoragePlugin(PluginBase):
|
||||
# Use singular name for consistency with BackupPlugin and GitService
|
||||
path = root / category.value
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
log.reason(f"Ensured directory: {path}")
|
||||
# #endregion ensure_directories
|
||||
logger.debug(f"[StoragePlugin][Action] Ensured directory: {path}")
|
||||
# [/DEF:ensure_directories:Function]
|
||||
|
||||
# #region validate_path [TYPE Function]
|
||||
# @BRIEF Prevents path traversal attacks by ensuring the path is within the storage root.
|
||||
# @PRE path must be a Path object.
|
||||
# @POST Returns the resolved absolute path if valid, otherwise raises ValueError.
|
||||
# [DEF:validate_path:Function]
|
||||
# @PURPOSE: Prevents path traversal attacks by ensuring the path is within the storage root.
|
||||
# @PRE: path must be a Path object.
|
||||
# @POST: Returns the resolved absolute path if valid, otherwise raises ValueError.
|
||||
def validate_path(self, path: Path) -> Path:
|
||||
with belief_scope("StoragePlugin:validate_path"):
|
||||
root = self.get_storage_root().resolve()
|
||||
@@ -198,13 +200,13 @@ class StoragePlugin(PluginBase):
|
||||
try:
|
||||
resolved.relative_to(root)
|
||||
except ValueError:
|
||||
log.explore(f"Path traversal detected: {resolved} is not under {root}", error="Path outside storage root")
|
||||
logger.error(f"[StoragePlugin][Coherence:Failed] Path traversal detected: {resolved} is not under {root}")
|
||||
raise ValueError("Access denied: Path is outside of storage root.")
|
||||
return resolved
|
||||
# #endregion validate_path
|
||||
# [/DEF:validate_path:Function]
|
||||
|
||||
# #region list_files [TYPE Function]
|
||||
# @BRIEF Lists all files and directories in a specific category and subpath.
|
||||
# [DEF:list_files:Function]
|
||||
# @PURPOSE: Lists all files and directories in a specific category and subpath.
|
||||
# @PARAM: category (Optional[FileCategory]) - The category to list.
|
||||
# @PARAM: subpath (Optional[str]) - Nested path within the category.
|
||||
# @PARAM: recursive (bool) - Whether to scan nested subdirectories recursively.
|
||||
@@ -219,8 +221,8 @@ class StoragePlugin(PluginBase):
|
||||
) -> List[StoredFile]:
|
||||
with belief_scope("StoragePlugin:list_files"):
|
||||
root = self.get_storage_root()
|
||||
log.reason(
|
||||
f"Listing files in root: {root}, category: {category}, subpath: {subpath}, recursive: {recursive}"
|
||||
logger.info(
|
||||
f"[StoragePlugin][Action] Listing files in root: {root}, category: {category}, subpath: {subpath}, recursive: {recursive}"
|
||||
)
|
||||
files = []
|
||||
|
||||
@@ -256,7 +258,7 @@ class StoragePlugin(PluginBase):
|
||||
if not target_dir.exists():
|
||||
continue
|
||||
|
||||
log.reason(f"Scanning directory: {target_dir}")
|
||||
logger.debug(f"[StoragePlugin][Action] Scanning directory: {target_dir}")
|
||||
|
||||
if recursive:
|
||||
for current_root, dirs, filenames in os.walk(target_dir):
|
||||
@@ -299,10 +301,10 @@ class StoragePlugin(PluginBase):
|
||||
|
||||
# Sort: directories first, then by name
|
||||
return sorted(files, key=lambda x: (x.mime_type != "directory", x.name))
|
||||
# #endregion list_files
|
||||
# [/DEF:list_files:Function]
|
||||
|
||||
# #region save_file [TYPE Function]
|
||||
# @BRIEF Saves an uploaded file to the specified category and optional subpath.
|
||||
# [DEF:save_file:Function]
|
||||
# @PURPOSE: Saves an uploaded file to the specified category and optional subpath.
|
||||
# @PARAM: file (UploadFile) - The uploaded file.
|
||||
# @PARAM: category (FileCategory) - The target category.
|
||||
# @PARAM: subpath (Optional[str]) - The target subpath.
|
||||
@@ -333,10 +335,10 @@ class StoragePlugin(PluginBase):
|
||||
category=category,
|
||||
mime_type=file.content_type
|
||||
)
|
||||
# #endregion save_file
|
||||
# [/DEF:save_file:Function]
|
||||
|
||||
# #region delete_file [TYPE Function]
|
||||
# @BRIEF Deletes a file or directory from the specified category and path.
|
||||
# [DEF:delete_file:Function]
|
||||
# @PURPOSE: Deletes a file or directory from the specified category and path.
|
||||
# @PARAM: category (FileCategory) - The category.
|
||||
# @PARAM: path (str) - The relative path of the file or directory.
|
||||
# @PRE: path must belong to the specified category and exist on disk.
|
||||
@@ -356,13 +358,13 @@ class StoragePlugin(PluginBase):
|
||||
shutil.rmtree(full_path)
|
||||
else:
|
||||
full_path.unlink()
|
||||
log.reflect(f"Deleted: {full_path}")
|
||||
logger.info(f"[StoragePlugin][Action] Deleted: {full_path}")
|
||||
else:
|
||||
raise FileNotFoundError(f"Item {path} not found")
|
||||
# #endregion delete_file
|
||||
# [/DEF:delete_file:Function]
|
||||
|
||||
# #region get_file_path [TYPE Function]
|
||||
# @BRIEF Returns the absolute path of a file for download.
|
||||
# [DEF:get_file_path:Function]
|
||||
# @PURPOSE: Returns the absolute path of a file for download.
|
||||
# @PARAM: category (FileCategory) - The category.
|
||||
# @PARAM: path (str) - The relative path of the file.
|
||||
# @PRE: path must belong to the specified category and be a file.
|
||||
@@ -380,7 +382,7 @@ class StoragePlugin(PluginBase):
|
||||
raise FileNotFoundError(f"File {path} not found")
|
||||
|
||||
return file_path
|
||||
# #endregion get_file_path
|
||||
# [/DEF:get_file_path:Function]
|
||||
|
||||
# #endregion StoragePlugin
|
||||
# #endregion StoragePlugin
|
||||
|
||||
Reference in New Issue
Block a user