RaBU1234 commited on
Commit
7a4e883
Β·
verified Β·
1 Parent(s): 6db5221

Create server.js

Browse files
Files changed (1) hide show
  1. server.js +184 -19
server.js CHANGED
@@ -8,6 +8,7 @@ const app = express();
8
  app.use(cors());
9
  app.use(express.json({ limit: '50mb' }));
10
 
 
11
  const sandboxes = new Map();
12
  const devServers = new Map();
13
 
@@ -16,6 +17,7 @@ app.get('/', (req, res) => {
16
  res.json({
17
  status: 'Sandbox server running',
18
  sandboxes: sandboxes.size,
 
19
  timestamp: new Date().toISOString()
20
  });
21
  });
@@ -24,31 +26,34 @@ app.get('/', (req, res) => {
24
  app.post('/api/sandbox/create', async (req, res) => {
25
  try {
26
  const sandboxId = `sandbox-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
27
- const sandboxPath = path.join('/tmp', sandboxId);
28
 
29
  await fs.mkdir(sandboxPath, { recursive: true });
 
30
  sandboxes.set(sandboxId, {
31
  path: sandboxPath,
32
- createdAt: Date.now()
 
33
  });
34
 
35
  console.log(`βœ… Sandbox created: ${sandboxId}`);
 
36
  res.json({
37
  sandboxId,
38
  status: 'ready',
39
  path: sandboxPath
40
  });
41
  } catch (error) {
42
- console.error('❌ Error:', error);
43
  res.status(500).json({ error: error.message });
44
  }
45
  });
46
 
47
- // Run command
48
  app.post('/api/sandbox/:id/command', async (req, res) => {
49
  try {
50
  const { id } = req.params;
51
- const { command, args = [] } = req.body;
52
  const sandbox = sandboxes.get(id);
53
 
54
  if (!sandbox) {
@@ -58,26 +63,75 @@ app.post('/api/sandbox/:id/command', async (req, res) => {
58
  const fullCommand = `cd ${sandbox.path} && ${command} ${args.join(' ')}`;
59
  console.log(`⚑ Executing: ${fullCommand}`);
60
 
61
- exec(fullCommand, {
62
- timeout: 300000,
63
- maxBuffer: 10 * 1024 * 1024
64
- }, (error, stdout, stderr) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  res.json({
66
- exitCode: error ? (error.code || 1) : 0,
67
- stdout: stdout.toString(),
68
- stderr: stderr.toString()
 
 
 
69
  });
70
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  } catch (error) {
 
72
  res.status(500).json({ error: error.message });
73
  }
74
  });
75
 
76
- // Write files
77
  app.post('/api/sandbox/:id/files', async (req, res) => {
78
  try {
79
  const { id } = req.params;
80
- const { files } = req.body;
81
  const sandbox = sandboxes.get(id);
82
 
83
  if (!sandbox) {
@@ -88,16 +142,127 @@ app.post('/api/sandbox/:id/files', async (req, res) => {
88
  const fullPath = path.join(sandbox.path, file.path);
89
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
90
  await fs.writeFile(fullPath, file.content, 'utf-8');
 
91
  }
92
 
93
- res.json({ success: true, filesWritten: files.length });
 
 
 
94
  } catch (error) {
 
95
  res.status(500).json({ error: error.message });
96
  }
97
  });
98
 
99
- // Start server on port 7860
100
- const PORT = process.env.PORT || 7860;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  app.listen(PORT, '0.0.0.0', () => {
102
- console.log(`πŸš€ Server running on port ${PORT}`);
 
103
  });
 
8
  app.use(cors());
9
  app.use(express.json({ limit: '50mb' }));
10
 
11
+ // Storage
12
  const sandboxes = new Map();
13
  const devServers = new Map();
14
 
 
17
  res.json({
18
  status: 'Sandbox server running',
19
  sandboxes: sandboxes.size,
20
+ devServers: devServers.size,
21
  timestamp: new Date().toISOString()
22
  });
23
  });
 
26
  app.post('/api/sandbox/create', async (req, res) => {
27
  try {
28
  const sandboxId = `sandbox-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
29
+ const sandboxPath = path.join('/tmp/sandboxes', sandboxId);
30
 
31
  await fs.mkdir(sandboxPath, { recursive: true });
32
+
33
  sandboxes.set(sandboxId, {
34
  path: sandboxPath,
35
+ createdAt: Date.now(),
36
+ port: null
37
  });
38
 
39
  console.log(`βœ… Sandbox created: ${sandboxId}`);
40
+
41
  res.json({
42
  sandboxId,
43
  status: 'ready',
44
  path: sandboxPath
45
  });
46
  } catch (error) {
47
+ console.error('❌ Sandbox creation error:', error);
48
  res.status(500).json({ error: error.message });
49
  }
50
  });
51
 
52
+ // Run command in sandbox
53
  app.post('/api/sandbox/:id/command', async (req, res) => {
54
  try {
55
  const { id } = req.params;
56
+ const { command, args = [], wait = true } = req.body;
57
  const sandbox = sandboxes.get(id);
58
 
59
  if (!sandbox) {
 
63
  const fullCommand = `cd ${sandbox.path} && ${command} ${args.join(' ')}`;
64
  console.log(`⚑ Executing: ${fullCommand}`);
65
 
66
+ // Check if it's a dev server command
67
+ const isDevServer = command.includes('npm run dev') ||
68
+ command.includes('vite') ||
69
+ command.includes('npm start');
70
+
71
+ if (isDevServer && !wait) {
72
+ // Assign dynamic port
73
+ const port = 3000 + devServers.size;
74
+ sandbox.port = port;
75
+
76
+ // Start dev server in background
77
+ const devProcess = spawn('sh', ['-c', `cd ${sandbox.path} && PORT=${port} ${command} ${args.join(' ')}`], {
78
+ detached: false,
79
+ stdio: ['ignore', 'pipe', 'pipe']
80
+ });
81
+
82
+ devServers.set(id, {
83
+ process: devProcess,
84
+ port,
85
+ startedAt: Date.now()
86
+ });
87
+
88
+ devProcess.stdout.on('data', (data) => {
89
+ console.log(`πŸ“¦ Dev server ${id}: ${data}`);
90
+ });
91
+
92
+ devProcess.stderr.on('data', (data) => {
93
+ console.error(`⚠️ Dev server ${id}: ${data}`);
94
+ });
95
+
96
+ devProcess.on('exit', (code) => {
97
+ console.log(`πŸ›‘ Dev server ${id} exited with code ${code}`);
98
+ devServers.delete(id);
99
+ });
100
+
101
  res.json({
102
+ exitCode: 0,
103
+ stdout: `Dev server started on port ${port}`,
104
+ stderr: '',
105
+ previewUrl: `/preview/${port}/`,
106
+ port: port,
107
+ commandId: `cmd-${Date.now()}`
108
  });
109
+ } else {
110
+ // Regular command execution
111
+ exec(fullCommand, {
112
+ timeout: 300000, // 5 minutes
113
+ maxBuffer: 10 * 1024 * 1024, // 10MB
114
+ cwd: sandbox.path
115
+ }, (error, stdout, stderr) => {
116
+ res.json({
117
+ exitCode: error ? (error.code || 1) : 0,
118
+ stdout: stdout.toString(),
119
+ stderr: stderr.toString(),
120
+ commandId: `cmd-${Date.now()}`
121
+ });
122
+ });
123
+ }
124
  } catch (error) {
125
+ console.error('❌ Command execution error:', error);
126
  res.status(500).json({ error: error.message });
127
  }
128
  });
129
 
130
+ // Write files to sandbox
131
  app.post('/api/sandbox/:id/files', async (req, res) => {
132
  try {
133
  const { id } = req.params;
134
+ const { files } = req.body; // Array of {path, content}
135
  const sandbox = sandboxes.get(id);
136
 
137
  if (!sandbox) {
 
142
  const fullPath = path.join(sandbox.path, file.path);
143
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
144
  await fs.writeFile(fullPath, file.content, 'utf-8');
145
+ console.log(`πŸ“ File written: ${file.path}`);
146
  }
147
 
148
+ res.json({
149
+ success: true,
150
+ filesWritten: files.length
151
+ });
152
  } catch (error) {
153
+ console.error('❌ File write error:', error);
154
  res.status(500).json({ error: error.message });
155
  }
156
  });
157
 
158
+ // Read file from sandbox
159
+ app.get('/api/sandbox/:id/files', async (req, res) => {
160
+ try {
161
+ const { id } = req.params;
162
+ const { path: filePath } = req.query;
163
+ const sandbox = sandboxes.get(id);
164
+
165
+ if (!sandbox) {
166
+ return res.status(404).json({ error: 'Sandbox not found' });
167
+ }
168
+
169
+ const fullPath = path.join(sandbox.path, filePath);
170
+ const content = await fs.readFile(fullPath, 'utf-8');
171
+
172
+ res.send(content);
173
+ } catch (error) {
174
+ console.error('❌ File read error:', error);
175
+ res.status(404).json({ error: 'File not found' });
176
+ }
177
+ });
178
+
179
+ // Get sandbox preview URL
180
+ app.get('/api/sandbox/:id/url', async (req, res) => {
181
+ try {
182
+ const { id } = req.params;
183
+ const sandbox = sandboxes.get(id);
184
+ const devServer = devServers.get(id);
185
+
186
+ if (!sandbox) {
187
+ return res.status(404).json({ error: 'Sandbox not found' });
188
+ }
189
+
190
+ if (devServer) {
191
+ const baseUrl = process.env.SPACE_HOST || 'http://localhost:7860';
192
+ res.json({
193
+ url: `${baseUrl}/preview/${devServer.port}/`,
194
+ port: devServer.port,
195
+ status: 'running',
196
+ uptime: Date.now() - devServer.startedAt
197
+ });
198
+ } else {
199
+ res.json({
200
+ url: null,
201
+ status: 'no dev server running'
202
+ });
203
+ }
204
+ } catch (error) {
205
+ res.status(500).json({ error: error.message });
206
+ }
207
+ });
208
+
209
+ // Delete sandbox
210
+ app.delete('/api/sandbox/:id', async (req, res) => {
211
+ try {
212
+ const { id } = req.params;
213
+ const sandbox = sandboxes.get(id);
214
+
215
+ if (!sandbox) {
216
+ return res.status(404).json({ error: 'Sandbox not found' });
217
+ }
218
+
219
+ // Kill dev server if running
220
+ const devServer = devServers.get(id);
221
+ if (devServer) {
222
+ devServer.process.kill();
223
+ devServers.delete(id);
224
+ }
225
+
226
+ // Delete sandbox directory
227
+ await fs.rm(sandbox.path, { recursive: true, force: true });
228
+ sandboxes.delete(id);
229
+
230
+ console.log(`πŸ—‘οΈ Sandbox deleted: ${id}`);
231
+ res.json({ success: true });
232
+ } catch (error) {
233
+ console.error('❌ Sandbox deletion error:', error);
234
+ res.status(500).json({ error: error.message });
235
+ }
236
+ });
237
+
238
+ // Cleanup old sandboxes every hour
239
+ setInterval(() => {
240
+ const now = Date.now();
241
+ const maxAge = 3600000; // 1 hour
242
+
243
+ for (const [id, sandbox] of sandboxes.entries()) {
244
+ if (now - sandbox.createdAt > maxAge) {
245
+ console.log(`🧹 Cleaning up old sandbox: ${id}`);
246
+
247
+ // Kill dev server
248
+ const devServer = devServers.get(id);
249
+ if (devServer) {
250
+ devServer.process.kill();
251
+ devServers.delete(id);
252
+ }
253
+
254
+ // Delete sandbox
255
+ fs.rm(sandbox.path, { recursive: true, force: true })
256
+ .catch(err => console.error(`Failed to delete sandbox ${id}:`, err));
257
+
258
+ sandboxes.delete(id);
259
+ }
260
+ }
261
+ }, 3600000);
262
+
263
+ // Start server
264
+ const PORT = process.env.PORT || 3001;
265
  app.listen(PORT, '0.0.0.0', () => {
266
+ console.log(`πŸš€ Sandbox API server running on port ${PORT}`);
267
+ console.log(`πŸ“Š Ready to create sandboxes`);
268
  });