sarveshpatel commited on
Commit
d54d7d2
·
verified ·
1 Parent(s): 3e107da

Create app/file_manager.py

Browse files
Files changed (1) hide show
  1. app/file_manager.py +142 -0
app/file_manager.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """File management for code storage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import uuid
7
+ from pathlib import Path
8
+
9
+ from app.config import Settings, get_settings
10
+ from app.models import FileResult
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class FileManager:
16
+ """Manages code files in the storage directory."""
17
+
18
+ def __init__(self, settings: Settings | None = None):
19
+ self._settings = settings or get_settings()
20
+ self._storage = Path(self._settings.code_storage_dir)
21
+ self._storage.mkdir(parents=True, exist_ok=True)
22
+
23
+ def _generate_filename(self, base: str | None = None) -> str:
24
+ """Generate a unique filename."""
25
+ suffix = uuid.uuid4().hex[:8]
26
+ base = base or "code"
27
+ # Sanitize
28
+ safe_base = "".join(c for c in base if c.isalnum() or c in ("_", "-"))[:100]
29
+ if not safe_base:
30
+ safe_base = "code"
31
+ return f"{safe_base}_{suffix}.py"
32
+
33
+ def _validate_path(self, file_path: str) -> Path | None:
34
+ """Validate that a file path is within the storage directory."""
35
+ try:
36
+ path = Path(file_path).resolve()
37
+ storage = self._storage.resolve()
38
+ if not str(path).startswith(str(storage)):
39
+ return None
40
+ return path
41
+ except (ValueError, OSError):
42
+ return None
43
+
44
+ def create_file(self, content: str, filename: str | None = None) -> FileResult:
45
+ """Create a new Python file with content."""
46
+ try:
47
+ if len(content.encode("utf-8")) > self._settings.max_file_size:
48
+ return FileResult(
49
+ success=False,
50
+ message=f"Content exceeds maximum file size of {self._settings.max_file_size} bytes",
51
+ )
52
+
53
+ name = self._generate_filename(filename)
54
+ file_path = self._storage / name
55
+ file_path.write_text(content, encoding="utf-8")
56
+
57
+ logger.info("Created file: %s", file_path)
58
+ return FileResult(
59
+ success=True,
60
+ file_path=str(file_path),
61
+ message=f"File created: {name}",
62
+ )
63
+ except Exception as e:
64
+ logger.exception("Error creating file")
65
+ return FileResult(success=False, message=f"Error creating file: {str(e)}")
66
+
67
+ def append_to_file(self, file_path: str, content: str) -> FileResult:
68
+ """Append content to an existing file."""
69
+ try:
70
+ path = self._validate_path(file_path)
71
+ if path is None:
72
+ return FileResult(
73
+ success=False,
74
+ file_path=file_path,
75
+ message="Invalid file path: must be within code storage directory",
76
+ )
77
+
78
+ if not path.exists():
79
+ return FileResult(
80
+ success=False,
81
+ file_path=file_path,
82
+ message=f"File not found: {file_path}",
83
+ )
84
+
85
+ current_size = path.stat().st_size
86
+ append_size = len(content.encode("utf-8"))
87
+ if current_size + append_size > self._settings.max_file_size:
88
+ return FileResult(
89
+ success=False,
90
+ file_path=file_path,
91
+ message=f"Appending would exceed maximum file size of {self._settings.max_file_size} bytes",
92
+ )
93
+
94
+ with path.open("a", encoding="utf-8") as f:
95
+ f.write(content)
96
+
97
+ logger.info("Appended to file: %s (%d bytes)", file_path, append_size)
98
+ return FileResult(
99
+ success=True,
100
+ file_path=file_path,
101
+ message=f"Appended {append_size} bytes to file",
102
+ )
103
+ except Exception as e:
104
+ logger.exception("Error appending to file")
105
+ return FileResult(
106
+ success=False,
107
+ file_path=file_path,
108
+ message=f"Error appending to file: {str(e)}",
109
+ )
110
+
111
+ def read_file(self, file_path: str) -> FileResult:
112
+ """Read content of an existing file."""
113
+ try:
114
+ path = self._validate_path(file_path)
115
+ if path is None:
116
+ return FileResult(
117
+ success=False,
118
+ file_path=file_path,
119
+ message="Invalid file path: must be within code storage directory",
120
+ )
121
+
122
+ if not path.exists():
123
+ return FileResult(
124
+ success=False,
125
+ file_path=file_path,
126
+ message=f"File not found: {file_path}",
127
+ )
128
+
129
+ content = path.read_text(encoding="utf-8")
130
+ return FileResult(
131
+ success=True,
132
+ file_path=file_path,
133
+ message="File read successfully",
134
+ content=content,
135
+ )
136
+ except Exception as e:
137
+ logger.exception("Error reading file")
138
+ return FileResult(
139
+ success=False,
140
+ file_path=file_path,
141
+ message=f"Error reading file: {str(e)}",
142
+ )