Передаем на тест

This commit is contained in:
2026-01-25 18:33:00 +03:00
parent a863807cf2
commit a542e7d2df
17 changed files with 954 additions and 40 deletions

View File

@@ -1 +1 @@
from . import plugins, tasks, settings, connections, environments, mappings, migration, git
from . import plugins, tasks, settings, connections, environments, mappings, migration, git, storage

View File

@@ -13,6 +13,7 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List
from ...core.config_models import AppConfig, Environment, GlobalSettings
from ...models.storage import StorageConfig
from ...dependencies import get_config_manager
from ...core.config_manager import ConfigManager
from ...core.logger import logger, belief_scope
@@ -56,6 +57,33 @@ async def update_global_settings(
return settings
# [/DEF:update_global_settings:Function]
# [DEF:get_storage_settings:Function]
# @PURPOSE: Retrieves storage-specific settings.
# @RETURN: StorageConfig - The storage configuration.
@router.get("/storage", response_model=StorageConfig)
async def get_storage_settings(config_manager: ConfigManager = Depends(get_config_manager)):
with belief_scope("get_storage_settings"):
return config_manager.get_config().settings.storage
# [/DEF:get_storage_settings:Function]
# [DEF:update_storage_settings:Function]
# @PURPOSE: Updates storage-specific settings.
# @PARAM: storage (StorageConfig) - The new storage settings.
# @POST: Storage settings are updated and saved.
# @RETURN: StorageConfig - The updated storage settings.
@router.put("/storage", response_model=StorageConfig)
async def update_storage_settings(storage: StorageConfig, config_manager: ConfigManager = Depends(get_config_manager)):
with belief_scope("update_storage_settings"):
is_valid, message = config_manager.validate_path(storage.root_path)
if not is_valid:
raise HTTPException(status_code=400, detail=message)
settings = config_manager.get_config().settings
settings.storage = storage
config_manager.update_global_settings(settings)
return config_manager.get_config().settings.storage
# [/DEF:update_storage_settings:Function]
# [DEF:get_environments:Function]
# @PURPOSE: Lists all configured Superset environments.
# @PRE: Config manager is available.

View File

@@ -0,0 +1,126 @@
# [DEF:storage_routes:Module]
#
# @SEMANTICS: storage, files, upload, download, backup, repository
# @PURPOSE: API endpoints for file storage management (backups and repositories).
# @LAYER: API
# @RELATION: DEPENDS_ON -> backend.src.models.storage
#
# @INVARIANT: All paths must be validated against path traversal.
# [SECTION: IMPORTS]
from fastapi import APIRouter, Depends, UploadFile, File, Form, HTTPException
from fastapi.responses import FileResponse
from typing import List, Optional
from backend.src.models.storage import StoredFile, FileCategory
from backend.src.dependencies import get_plugin_loader
from backend.src.plugins.storage.plugin import StoragePlugin
from backend.src.core.logger import belief_scope
# [/SECTION]
router = APIRouter(tags=["storage"])
# [DEF:list_files:Function]
# @PURPOSE: List all files in the storage system, optionally filtered by category.
#
# @PRE: None.
# @POST: Returns a list of StoredFile objects.
#
# @PARAM: category (Optional[FileCategory]) - Filter by category.
# @RETURN: List[StoredFile] - List of files.
#
# @RELATION: CALLS -> StoragePlugin.list_files
@router.get("/files", response_model=List[StoredFile])
async def list_files(category: Optional[FileCategory] = None, plugin_loader=Depends(get_plugin_loader)):
with belief_scope("list_files"):
storage_plugin: StoragePlugin = plugin_loader.get_plugin("storage-manager")
if not storage_plugin:
raise HTTPException(status_code=500, detail="Storage plugin not loaded")
return storage_plugin.list_files(category)
# [/DEF:list_files:Function]
# [DEF:upload_file:Function]
# @PURPOSE: Upload a file to the storage system under a specific category.
#
# @PRE: category must be a valid FileCategory.
# @PRE: file must be a valid UploadFile.
# @POST: Returns the StoredFile object of the uploaded file.
#
# @PARAM: category (FileCategory) - Target category.
# @PARAM: file (UploadFile) - The file content.
# @RETURN: StoredFile - Metadata of the uploaded file.
#
# @SIDE_EFFECT: Writes file to the filesystem.
#
# @RELATION: CALLS -> StoragePlugin.save_file
@router.post("/upload", response_model=StoredFile, status_code=201)
async def upload_file(
category: FileCategory = Form(...),
file: UploadFile = File(...),
plugin_loader=Depends(get_plugin_loader)
):
with belief_scope("upload_file"):
storage_plugin: StoragePlugin = plugin_loader.get_plugin("storage-manager")
if not storage_plugin:
raise HTTPException(status_code=500, detail="Storage plugin not loaded")
try:
return await storage_plugin.save_file(file, category)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# [/DEF:upload_file:Function]
# [DEF:delete_file:Function]
# @PURPOSE: Delete a specific file from the storage system.
#
# @PRE: category must be a valid FileCategory.
# @PRE: filename must not contain path separators.
# @POST: File is removed from storage.
#
# @PARAM: category (FileCategory) - File category.
# @PARAM: filename (str) - Name of the file.
# @RETURN: None
#
# @SIDE_EFFECT: Deletes file from the filesystem.
#
# @RELATION: CALLS -> StoragePlugin.delete_file
@router.delete("/files/{category}/{filename}", status_code=204)
async def delete_file(category: FileCategory, filename: str, plugin_loader=Depends(get_plugin_loader)):
with belief_scope("delete_file"):
storage_plugin: StoragePlugin = plugin_loader.get_plugin("storage-manager")
if not storage_plugin:
raise HTTPException(status_code=500, detail="Storage plugin not loaded")
try:
storage_plugin.delete_file(category, filename)
except FileNotFoundError:
raise HTTPException(status_code=404, detail="File not found")
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# [/DEF:delete_file:Function]
# [DEF:download_file:Function]
# @PURPOSE: Retrieve a file for download.
#
# @PRE: category must be a valid FileCategory.
# @PRE: filename must exist in the specified category.
# @POST: Returns a FileResponse.
#
# @PARAM: category (FileCategory) - File category.
# @PARAM: filename (str) - Name of the file.
# @RETURN: FileResponse - The file content.
#
# @RELATION: CALLS -> StoragePlugin.get_file_path
@router.get("/download/{category}/{filename}")
async def download_file(category: FileCategory, filename: str, plugin_loader=Depends(get_plugin_loader)):
with belief_scope("download_file"):
storage_plugin: StoragePlugin = plugin_loader.get_plugin("storage-manager")
if not storage_plugin:
raise HTTPException(status_code=500, detail="Storage plugin not loaded")
try:
path = storage_plugin.get_file_path(category, filename)
return FileResponse(path=path, filename=filename)
except FileNotFoundError:
raise HTTPException(status_code=404, detail="File not found")
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# [/DEF:download_file:Function]
# [/DEF:storage_routes:Module]