Добавлен workspace file engine для DevConsole
Этот коммит содержится в:
@@ -0,0 +1,75 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
DATA_DIR = Path(os.getenv('DEVCONSOLE_DATA_DIR', '/var/lib/devconsole'))
|
||||
PROJECTS_DIR = DATA_DIR / 'projects'
|
||||
|
||||
|
||||
class WorkspaceSecurityError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def ensure_workspace(path: str) -> Path:
|
||||
workspace = Path(path).resolve()
|
||||
|
||||
try:
|
||||
workspace.relative_to(PROJECTS_DIR.resolve())
|
||||
except ValueError as exc:
|
||||
raise WorkspaceSecurityError(
|
||||
'Workspace path is outside projects directory'
|
||||
) from exc
|
||||
|
||||
return workspace
|
||||
|
||||
|
||||
def build_tree(path: str, max_depth: int = 3):
|
||||
workspace = ensure_workspace(path)
|
||||
|
||||
def scan(directory: Path, depth: int = 0):
|
||||
if depth > max_depth:
|
||||
return []
|
||||
|
||||
result = []
|
||||
|
||||
for item in sorted(directory.iterdir(), key=lambda x: (x.is_file(), x.name.lower())):
|
||||
if item.name.startswith('.'):
|
||||
continue
|
||||
|
||||
node = {
|
||||
'name': item.name,
|
||||
'path': item.as_posix(),
|
||||
'type': 'directory' if item.is_dir() else 'file',
|
||||
}
|
||||
|
||||
if item.is_dir():
|
||||
node['children'] = scan(item, depth + 1)
|
||||
|
||||
result.append(node)
|
||||
|
||||
return result
|
||||
|
||||
return scan(workspace)
|
||||
|
||||
|
||||
def read_file(path: str):
|
||||
file_path = ensure_workspace(path)
|
||||
|
||||
if file_path.is_dir():
|
||||
raise WorkspaceSecurityError('Cannot read directory')
|
||||
|
||||
return file_path.read_text(encoding='utf-8', errors='ignore')
|
||||
|
||||
|
||||
def save_file(path: str, content: str):
|
||||
file_path = ensure_workspace(path)
|
||||
|
||||
if file_path.is_dir():
|
||||
raise WorkspaceSecurityError('Cannot save directory')
|
||||
|
||||
file_path.write_text(content, encoding='utf-8')
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'path': file_path.as_posix(),
|
||||
'size': len(content),
|
||||
}
|
||||
Ссылка в новой задаче
Block a user