Skip to content

Commit e7b96f7

Browse files
committed
Add deploy to backend
1 parent 66ed284 commit e7b96f7

File tree

2 files changed

+137
-14
lines changed

2 files changed

+137
-14
lines changed

backend/main.py

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
from flask import Flask, request, jsonify
1+
from flask import Flask, request, jsonify, send_from_directory
22
from flask_restful import Resource, Api
33
from flask_sqlalchemy import SQLAlchemy
44
import json
55
import os
6+
import argparse
7+
import zipfile
8+
import shutil
9+
from werkzeug.utils import secure_filename
610

7-
app = Flask(__name__)
11+
app = Flask(__name__, static_folder='../dist', static_url_path='')
12+
app.url_map.merge_slashes = False # Don't merge consecutive slashes
813
api = Api(app)
914

1015
# Add CORS headers
@@ -224,25 +229,143 @@ def get(self):
224229

225230
return {'files': sorted(list(children))}
226231

232+
class DeployResource(Resource):
233+
def post(self):
234+
"""Upload and extract a zip file to the deploy directory"""
235+
if 'file' not in request.files:
236+
return {'error': 'No file provided'}, 400
237+
238+
file = request.files['file']
239+
240+
if file.filename == '':
241+
return {'error': 'No file selected'}, 400
242+
243+
if not file.filename.endswith('.zip'):
244+
return {'error': 'Only zip files are allowed'}, 400
245+
246+
try:
247+
# Create deploy directory if it doesn't exist
248+
deploy_dir = os.path.join(basedir, 'deploy')
249+
250+
# Clear existing deploy directory
251+
if os.path.exists(deploy_dir):
252+
shutil.rmtree(deploy_dir)
253+
os.makedirs(deploy_dir)
254+
255+
# Save the zip file temporarily
256+
temp_zip_path = os.path.join(basedir, 'temp_deploy.zip')
257+
file.save(temp_zip_path)
258+
259+
# Extract the zip file
260+
with zipfile.ZipFile(temp_zip_path, 'r') as zip_ref:
261+
zip_ref.extractall(deploy_dir)
262+
263+
# Remove the temporary zip file
264+
os.remove(temp_zip_path)
265+
266+
# List extracted files
267+
extracted_files = []
268+
for root, dirs, files in os.walk(deploy_dir):
269+
for filename in files:
270+
rel_path = os.path.relpath(os.path.join(root, filename), deploy_dir)
271+
extracted_files.append(rel_path)
272+
273+
return {
274+
'message': 'Deployment successful',
275+
'deploy_directory': deploy_dir,
276+
'files_extracted': len(extracted_files),
277+
'files': extracted_files[:20] # Show first 20 files
278+
}
279+
except zipfile.BadZipFile:
280+
return {'error': 'Invalid zip file'}, 400
281+
except Exception as e:
282+
return {'error': f'Deployment failed: {str(e)}'}, 500
283+
227284
# Register API routes
228285
# Storage API routes (more specific routes first)
229286
api.add_resource(StorageEntryResource, '/entries/<path:entry_key>')
230287
api.add_resource(StorageFileRenameResource, '/storage/rename')
231288
api.add_resource(StorageRootResource, '/storage/')
232289
api.add_resource(StorageResource, '/storage/<path:path>')
290+
api.add_resource(DeployResource, '/deploy')
233291

234-
@app.route('/')
235-
def index():
236-
return jsonify({
237-
'message': 'Storage API',
238-
'endpoints': {
239-
'entries': '/entries/<entry_key>',
240-
'storage': '/storage/<path>',
241-
'storage_rename': '/storage/rename'
242-
}
243-
})
292+
# Handle the base path for the frontend
293+
@app.route('/blocks/', defaults={'path': ''})
294+
@app.route('/blocks/<path:path>')
295+
@app.route('/blocks//<path:path>') # Handle double slash
296+
def serve_frontend(path):
297+
"""Serve static assets from dist/ directory with base path"""
298+
# Normalize path - remove leading slashes and clean up double slashes
299+
path = path.lstrip('/')
300+
301+
# Debug logging
302+
print(f"Requested path: '{path}'")
303+
304+
# If path is empty, serve index.html
305+
if path == '':
306+
try:
307+
return send_from_directory(app.static_folder, 'index.html')
308+
except Exception as e:
309+
print(f"Error serving index.html: {e}")
310+
return jsonify({
311+
'error': 'Frontend not built',
312+
'message': 'Please build the frontend first with "npm run build"'
313+
}), 404
314+
315+
# Try to serve the requested file
316+
try:
317+
print(f"Attempting to serve: {app.static_folder}/{path}")
318+
return send_from_directory(app.static_folder, path)
319+
except Exception as e:
320+
print(f"Error serving file: {e}")
321+
# If file not found and not an asset, serve index.html for client-side routing
322+
# But if it's an asset or known file type, return 404
323+
if path.startswith('assets/') or '.' in path.split('/')[-1]:
324+
return jsonify({'error': f'File not found: {path}'}), 404
325+
try:
326+
return send_from_directory(app.static_folder, 'index.html')
327+
except:
328+
return jsonify({'error': 'File not found'}), 404
329+
330+
@app.route('/', defaults={'path': ''})
331+
@app.route('/<path:path>')
332+
def serve_static(path):
333+
"""Serve static assets from dist/ directory"""
334+
# If path is empty, serve index.html
335+
if path == '':
336+
try:
337+
return send_from_directory(app.static_folder, 'index.html')
338+
except Exception as e:
339+
return jsonify({
340+
'error': 'Frontend not built',
341+
'message': 'Please build the frontend first with "npm run build"',
342+
'api_info': {
343+
'endpoints': {
344+
'entries': '/entries/<entry_key>',
345+
'storage': '/storage/<path>',
346+
'storage_rename': '/storage/rename'
347+
}
348+
}
349+
}), 404
350+
351+
# Try to serve the requested file
352+
try:
353+
return send_from_directory(app.static_folder, path)
354+
except Exception as e:
355+
# If file not found, serve index.html for client-side routing
356+
try:
357+
return send_from_directory(app.static_folder, 'index.html')
358+
except:
359+
return jsonify({'error': 'File not found'}), 404
244360

245361
if __name__ == '__main__':
362+
parser = argparse.ArgumentParser(description='Run the Storage API backend server')
363+
parser.add_argument('-p', '--port', type=int, default=5001,
364+
help='Port to run the server on (default: 5001)')
365+
args = parser.parse_args()
366+
246367
with app.app_context():
247368
db.create_all()
248-
app.run(debug=True, port=5001)
369+
370+
print(f"Starting server on port {args.port}...")
371+
app.run(debug=True, port=args.port)

vite.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import react from "@vitejs/plugin-react";
77
import tsconfigPaths from "vite-tsconfig-paths";
88

99
export default defineConfig({
10-
base: '/systemcore-blocks-interface/',
10+
base: '/blocks/',
1111
plugins: [react(), tsconfigPaths(), viteStaticCopy({
1212
targets: [
1313
{

0 commit comments

Comments
 (0)