Skip to content

Commit 39770a2

Browse files
committed
Auto save modal removed, import file function and button added for admin, repl title changed, text size for repl increased, run button colors removed, left sidebar Heading removed
1 parent d2b8a72 commit 39770a2

17 files changed

Lines changed: 1110 additions & 59 deletions
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/static/css/659.48591cc8.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

dist/static/css/709.412a5eb0.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/static/js/659.2d903cdc.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

dist/static/js/709.525eb768.js

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/svg+xml" href="python-icon.svg"><link rel="alternate icon" href="python-icon.svg"><link rel="shortcut icon" href="python-icon.svg"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet"><script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script><link href="/static/css/prism-tomorrow-consistent.css" rel="stylesheet"/><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script><title>CS3 IDE</title><script defer="defer" src="static/js/chunk-vendors.66f3d13d.js"></script><script defer="defer" src="static/js/app.a0fdc8b5.js"></script><link href="static/css/chunk-vendors.6b1238e4.css" rel="stylesheet"><link href="static/css/app.94ef51a2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Python Web IDE doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
1+
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/svg+xml" href="python-icon.svg"><link rel="alternate icon" href="python-icon.svg"><link rel="shortcut icon" href="python-icon.svg"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet"><script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script><link href="/static/css/prism-tomorrow-consistent.css" rel="stylesheet"/><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script><title>CS3 IDE</title><script defer="defer" src="static/js/chunk-vendors.66f3d13d.js"></script><script defer="defer" src="static/js/app.9f71ed26.js"></script><link href="static/css/chunk-vendors.6b1238e4.css" rel="stylesheet"><link href="static/css/app.94ef51a2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Python Web IDE doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

server/handlers/upload_handler.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import tornado.web
5+
import sys
6+
import os
7+
import logging
8+
import mimetypes
9+
from pathlib import Path
10+
11+
# Add parent directory to path for imports
12+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13+
14+
from auth.user_manager_postgres import UserManager
15+
from common.file_storage import file_storage
16+
from common.config import Config
17+
from command.resource import write_project_file
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
class UploadFileHandler(tornado.web.RequestHandler):
23+
"""Handle file upload requests for admin users"""
24+
25+
def initialize(self):
26+
self.user_manager = UserManager()
27+
28+
def set_default_headers(self):
29+
"""Set CORS headers"""
30+
self.set_header("Access-Control-Allow-Origin", "*")
31+
self.set_header("Access-Control-Allow-Methods", "POST, OPTIONS")
32+
self.set_header("Access-Control-Allow-Headers", "Content-Type, session-id")
33+
34+
def options(self):
35+
"""Handle preflight requests"""
36+
self.set_status(204)
37+
self.finish()
38+
39+
def post(self):
40+
"""Handle file upload POST request"""
41+
try:
42+
# Get session ID from headers
43+
session_id = self.request.headers.get('session-id')
44+
if not session_id:
45+
self.set_status(401)
46+
self.write(json.dumps({
47+
'success': False,
48+
'error': 'No session ID provided'
49+
}))
50+
return
51+
52+
# Validate session and get user info
53+
user_info = self.user_manager.validate_session(session_id)
54+
if not user_info:
55+
self.set_status(401)
56+
self.write(json.dumps({
57+
'success': False,
58+
'error': 'Invalid session'
59+
}))
60+
return
61+
62+
# Check if user is admin (one of the specified admin accounts)
63+
admin_accounts = ['sl7927', 'sa9082', 'et2434']
64+
if user_info['username'] not in admin_accounts:
65+
self.set_status(403)
66+
self.write(json.dumps({
67+
'success': False,
68+
'error': 'Only admin users can upload files'
69+
}))
70+
return
71+
72+
# Get form data
73+
project_name = self.get_argument('projectName', default=None)
74+
parent_path = self.get_argument('parentPath', default='/')
75+
filename = self.get_argument('filename', default=None)
76+
77+
if not project_name or not filename:
78+
self.set_status(400)
79+
self.write(json.dumps({
80+
'success': False,
81+
'error': 'Missing required parameters: projectName, filename'
82+
}))
83+
return
84+
85+
# Get uploaded file
86+
if 'file' not in self.request.files:
87+
self.set_status(400)
88+
self.write(json.dumps({
89+
'success': False,
90+
'error': 'No file uploaded'
91+
}))
92+
return
93+
94+
file_info = self.request.files['file'][0]
95+
file_content = file_info['body']
96+
97+
# Validate file extension
98+
allowed_extensions = ['.py', '.txt', '.csv', '.pdf']
99+
file_extension = '.' + filename.split('.')[-1].lower()
100+
if file_extension not in allowed_extensions:
101+
self.set_status(400)
102+
self.write(json.dumps({
103+
'success': False,
104+
'error': f'File type not allowed. Supported: {", ".join(allowed_extensions)}'
105+
}))
106+
return
107+
108+
# Validate file size (10MB limit)
109+
max_size = 10 * 1024 * 1024 # 10MB
110+
if len(file_content) > max_size:
111+
self.set_status(400)
112+
self.write(json.dumps({
113+
'success': False,
114+
'error': 'File too large. Maximum size: 10MB'
115+
}))
116+
return
117+
118+
# Clean up filename (remove any path separators)
119+
safe_filename = os.path.basename(filename)
120+
121+
# Construct the full path
122+
if parent_path == '/':
123+
file_path = f"/{safe_filename}"
124+
else:
125+
file_path = f"{parent_path}/{safe_filename}"
126+
127+
# Use the same file writing logic as the existing IDE
128+
try:
129+
# Get project path
130+
project_path = os.path.join(file_storage.ide_base, project_name)
131+
full_file_path = os.path.join(project_path, file_path.lstrip('/'))
132+
133+
# Ensure parent directory exists
134+
os.makedirs(os.path.dirname(full_file_path), exist_ok=True)
135+
136+
# Determine if it's a binary file
137+
is_binary = self._is_binary_file(file_extension)
138+
139+
if is_binary:
140+
# For binary files (like PDFs), save raw content
141+
with open(full_file_path, 'wb') as f:
142+
f.write(file_content)
143+
code = 0
144+
else:
145+
# For text files, decode content and use write_project_file
146+
try:
147+
content_str = file_content.decode('utf-8')
148+
except UnicodeDecodeError:
149+
# Try with other encodings
150+
try:
151+
content_str = file_content.decode('latin-1')
152+
except UnicodeDecodeError:
153+
content_str = file_content.decode('utf-8', errors='replace')
154+
155+
# Use the existing write_project_file function
156+
code, error = write_project_file(project_path, full_file_path, content_str)
157+
158+
if code == 0:
159+
logger.info(f"File uploaded successfully: {project_name}{file_path} by {user_info['username']}")
160+
self.write(json.dumps({
161+
'success': True,
162+
'message': f'File {safe_filename} uploaded successfully',
163+
'path': file_path,
164+
'project': project_name
165+
}))
166+
else:
167+
raise Exception(f"Failed to save file: error code {code}")
168+
169+
except Exception as e:
170+
logger.error(f"Error saving uploaded file: {str(e)}")
171+
self.set_status(500)
172+
self.write(json.dumps({
173+
'success': False,
174+
'error': f'Failed to save file: {str(e)}'
175+
}))
176+
return
177+
178+
except Exception as e:
179+
logger.error(f"Error in UploadFileHandler: {str(e)}")
180+
self.set_status(500)
181+
self.write(json.dumps({
182+
'success': False,
183+
'error': 'Internal server error'
184+
}))
185+
186+
def _is_binary_file(self, extension):
187+
"""Determine if a file should be treated as binary based on its extension"""
188+
binary_extensions = ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.zip', '.tar', '.gz']
189+
return extension.lower() in binary_extensions

0 commit comments

Comments
 (0)