Skip to content

Commit 59f1cfa

Browse files
committed
split storage into its own file
1 parent db43fee commit 59f1cfa

File tree

2 files changed

+202
-198
lines changed

2 files changed

+202
-198
lines changed

backend/main.py

Lines changed: 3 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
# Standard library imports
22
import argparse
3-
import json
43
import os
54

65
# Third-party imports
76
from flask import Flask, jsonify, request, send_from_directory
8-
from flask_restful import Api, Resource
7+
from flask_restful import Api
98
from flask_sqlalchemy import SQLAlchemy
10-
from werkzeug.utils import secure_filename
119

1210
# Our imports
1311
from deploy import DeployResource
12+
from storage import StorageEntryResource, StorageFileRenameResource, StorageRootResource, StorageResource
1413

1514
app = Flask(__name__, static_folder='../dist', static_url_path='')
1615
app.url_map.merge_slashes = False # Don't merge consecutive slashes
1716
api = Api(app)
17+
db = SQLAlchemy(app)
1818

1919
# Add CORS headers
2020
@app.after_request
@@ -38,201 +38,6 @@ def handle_preflight():
3838
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(basedir, "projects.db")}'
3939
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
4040

41-
db = SQLAlchemy(app)
42-
43-
# Storage models for key-value pairs and files
44-
class StorageEntry(db.Model):
45-
id = db.Column(db.Integer, primary_key=True)
46-
entry_key = db.Column(db.String(255), nullable=False, unique=True)
47-
entry_value = db.Column(db.Text, nullable=False)
48-
49-
def to_dict(self):
50-
return {
51-
'key': self.entry_key,
52-
'value': self.entry_value
53-
}
54-
55-
class StorageFile(db.Model):
56-
id = db.Column(db.Integer, primary_key=True)
57-
file_path = db.Column(db.String(500), nullable=False, unique=True)
58-
file_content = db.Column(db.Text, nullable=False)
59-
60-
def to_dict(self):
61-
return {
62-
'path': self.file_path,
63-
'content': self.file_content
64-
}
65-
66-
# Storage Resources for key-value and file operations
67-
class StorageEntryResource(Resource):
68-
def get(self, entry_key):
69-
"""Fetch entry value by key"""
70-
entry = StorageEntry.query.filter_by(entry_key=entry_key).first()
71-
if entry:
72-
return {'value': entry.entry_value}
73-
else:
74-
# Return default value if provided in query params
75-
default_value = request.args.get('default', '')
76-
return {'value': default_value}
77-
78-
def post(self, entry_key):
79-
"""Save entry value"""
80-
data = request.get_json()
81-
if not data or 'value' not in data:
82-
return {'error': 'Entry value is required'}, 400
83-
84-
entry = StorageEntry.query.filter_by(entry_key=entry_key).first()
85-
if entry:
86-
entry.entry_value = data['value']
87-
else:
88-
entry = StorageEntry(entry_key=entry_key, entry_value=data['value'])
89-
db.session.add(entry)
90-
91-
try:
92-
db.session.commit()
93-
return {'message': 'Entry saved successfully'}
94-
except Exception as e:
95-
db.session.rollback()
96-
return {'error': 'Failed to save entry'}, 500
97-
98-
class StorageResource(Resource):
99-
def get(self, path):
100-
"""Get file content or list directory based on path"""
101-
# Handle empty path as root directory
102-
if not path:
103-
path = ""
104-
105-
if path.endswith('/') or path == "":
106-
# List directory
107-
# For root directory, find all files
108-
if path == "":
109-
files = StorageFile.query.all()
110-
# Extract top-level files and directories
111-
children = set()
112-
for file in files:
113-
if '/' in file.file_path:
114-
# It's in a subdirectory, add the directory name
115-
dir_name = file.file_path.split('/')[0] + '/'
116-
children.add(dir_name)
117-
else:
118-
# It's a top-level file
119-
children.add(file.file_path)
120-
else:
121-
# Find all files that start with this path
122-
files = StorageFile.query.filter(StorageFile.file_path.like(f'{path}%')).all()
123-
124-
# Extract immediate children (not nested)
125-
children = set()
126-
for file in files:
127-
relative_path = file.file_path[len(path):]
128-
if '/' in relative_path:
129-
# It's in a subdirectory, add the directory name
130-
dir_name = relative_path.split('/')[0] + '/'
131-
children.add(dir_name)
132-
else:
133-
# It's a direct child file
134-
children.add(relative_path)
135-
136-
return {'files': sorted(list(children))}
137-
else:
138-
# Get file content
139-
file_obj = StorageFile.query.filter_by(file_path=path).first()
140-
if file_obj:
141-
return {'content': file_obj.file_content}
142-
else:
143-
return {'error': 'File not found'}, 404
144-
145-
def post(self, path):
146-
"""Save file content (only for files, not directories)"""
147-
if path.endswith('/'):
148-
return {'error': 'Cannot save content to a directory path'}, 400
149-
150-
data = request.get_json()
151-
if not data or 'content' not in data:
152-
return {'error': 'File content is required'}, 400
153-
154-
file_obj = StorageFile.query.filter_by(file_path=path).first()
155-
if file_obj:
156-
file_obj.file_content = data['content']
157-
else:
158-
file_obj = StorageFile(file_path=path, file_content=data['content'])
159-
db.session.add(file_obj)
160-
161-
try:
162-
db.session.commit()
163-
return {'message': 'File saved successfully'}
164-
except Exception as e:
165-
db.session.rollback()
166-
return {'error': 'Failed to save file'}, 500
167-
168-
def delete(self, path):
169-
"""Delete file or directory"""
170-
if path.endswith('/'):
171-
# Delete directory (all files starting with this path)
172-
files = StorageFile.query.filter(StorageFile.file_path.like(f'{path}%')).all()
173-
for file_obj in files:
174-
db.session.delete(file_obj)
175-
else:
176-
# Delete specific file
177-
file_obj = StorageFile.query.filter_by(file_path=path).first()
178-
if not file_obj:
179-
return {'error': 'File not found'}, 404
180-
db.session.delete(file_obj)
181-
182-
try:
183-
db.session.commit()
184-
return {'message': 'Deleted successfully'}
185-
except Exception as e:
186-
db.session.rollback()
187-
return {'error': 'Failed to delete'}, 500
188-
189-
class StorageFileRenameResource(Resource):
190-
def post(self):
191-
"""Rename file or directory"""
192-
data = request.get_json()
193-
if not data or 'old_path' not in data or 'new_path' not in data:
194-
return {'error': 'Both old_path and new_path are required'}, 400
195-
196-
old_path = data['old_path']
197-
new_path = data['new_path']
198-
199-
if old_path.endswith('/'):
200-
# Rename directory (all files starting with old_path)
201-
files = StorageFile.query.filter(StorageFile.file_path.like(f'{old_path}%')).all()
202-
for file_obj in files:
203-
new_file_path = file_obj.file_path.replace(old_path, new_path, 1)
204-
file_obj.file_path = new_file_path
205-
else:
206-
# Rename specific file
207-
file_obj = StorageFile.query.filter_by(file_path=old_path).first()
208-
if not file_obj:
209-
return {'error': 'File not found'}, 404
210-
file_obj.file_path = new_path
211-
212-
try:
213-
db.session.commit()
214-
return {'message': 'Renamed successfully'}
215-
except Exception as e:
216-
db.session.rollback()
217-
return {'error': 'Failed to rename'}, 500
218-
219-
class StorageRootResource(Resource):
220-
def get(self):
221-
"""List all top-level files and directories"""
222-
files = StorageFile.query.all()
223-
# Extract top-level files and directories
224-
children = set()
225-
for file in files:
226-
if '/' in file.file_path:
227-
# It's in a subdirectory, add the directory name
228-
dir_name = file.file_path.split('/')[0] + '/'
229-
children.add(dir_name)
230-
else:
231-
# It's a top-level file
232-
children.add(file.file_path)
233-
234-
return {'files': sorted(list(children))}
235-
23641
# Register API routes
23742
# Storage API routes (more specific routes first)
23843
api.add_resource(StorageEntryResource, '/entries/<path:entry_key>')

0 commit comments

Comments
 (0)