Japrax19 commited on
Commit
3b3695b
·
verified ·
1 Parent(s): a212323

Upload 3 files

Browse files
Files changed (3) hide show
  1. backend/file_manager.py +30 -0
  2. backend/main.py +86 -0
  3. backend/terminal.py +65 -0
backend/file_manager.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ BASE_DIR = "/tmp/workspaces"
5
+
6
+ class FileManager:
7
+ def __init__(self, session_id):
8
+ self.session_dir = os.path.join(BASE_DIR, session_id)
9
+ os.makedirs(self.session_dir, exist_ok=True)
10
+
11
+ def save(self, filename, content):
12
+ path = os.path.join(self.session_dir, filename)
13
+ os.makedirs(os.path.dirname(path), exist_ok=True)
14
+ with open(path, "w") as f:
15
+ f.write(content)
16
+
17
+ def load(self, filename):
18
+ path = os.path.join(self.session_dir, filename)
19
+ if os.path.exists(path):
20
+ with open(path, "r") as f:
21
+ return f.read()
22
+ return None
23
+
24
+ def list_files(self):
25
+ files = []
26
+ for root, dirs, filenames in os.walk(self.session_dir):
27
+ rel = os.path.relpath(root, self.session_dir)
28
+ for f in filenames:
29
+ files.append(os.path.join(rel, f))
30
+ return files
backend/main.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
4
+ from fastapi.responses import FileResponse, JSONResponse
5
+ from fastapi.staticfiles import StaticFiles
6
+ from pydantic import BaseModel
7
+ from typing import Optional
8
+ import uuid
9
+
10
+ from .ai_model import generate, generate_with_debug
11
+ from .terminal import TerminalManager
12
+ from .file_manager import FileManager
13
+
14
+ app = FastAPI()
15
+
16
+ # Mount static files (hasil build React)
17
+ app.mount("/static", StaticFiles(directory="static"), name="static")
18
+
19
+ # Manager untuk terminal sessions
20
+ term_manager = TerminalManager()
21
+ file_managers = {}
22
+
23
+ class GenerateRequest(BaseModel):
24
+ prompt: str
25
+ debug: bool = False
26
+
27
+ class SaveFileRequest(BaseModel):
28
+ session_id: str
29
+ filename: str
30
+ content: str
31
+
32
+ class LoadFileRequest(BaseModel):
33
+ session_id: str
34
+ filename: str
35
+
36
+ @app.get("/")
37
+ async def serve_frontend():
38
+ return FileResponse("static/index.html")
39
+
40
+ @app.post("/api/generate")
41
+ async def api_generate(req: GenerateRequest):
42
+ try:
43
+ if req.debug:
44
+ code = generate_with_debug(req.prompt)
45
+ else:
46
+ code = generate(req.prompt)
47
+ return {"code": code}
48
+ except Exception as e:
49
+ raise HTTPException(status_code=500, detail=str(e))
50
+
51
+ @app.post("/api/files/save")
52
+ async def save_file(req: SaveFileRequest):
53
+ if req.session_id not in file_managers:
54
+ file_managers[req.session_id] = FileManager(req.session_id)
55
+ fm = file_managers[req.session_id]
56
+ fm.save(req.filename, req.content)
57
+ return {"status": "ok"}
58
+
59
+ @app.get("/api/files/{session_id}/{filename:path}")
60
+ async def load_file(session_id: str, filename: str):
61
+ if session_id not in file_managers:
62
+ file_managers[session_id] = FileManager(session_id)
63
+ fm = file_managers[session_id]
64
+ content = fm.load(filename)
65
+ if content is None:
66
+ raise HTTPException(status_code=404, detail="File not found")
67
+ return {"content": content}
68
+
69
+ @app.get("/api/files/{session_id}")
70
+ async def list_files(session_id: str):
71
+ if session_id not in file_managers:
72
+ file_managers[session_id] = FileManager(session_id)
73
+ fm = file_managers[session_id]
74
+ return {"files": fm.list_files()}
75
+
76
+ @app.websocket("/ws/terminal/{session_id}")
77
+ async def websocket_terminal(websocket: WebSocket, session_id: str):
78
+ await websocket.accept()
79
+ term = term_manager.get_session(session_id)
80
+ term.add_websocket(websocket)
81
+ try:
82
+ while True:
83
+ command = await websocket.receive_text()
84
+ term.write(command)
85
+ except WebSocketDisconnect:
86
+ term.remove_websocket(websocket)
backend/terminal.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pty
3
+ import select
4
+ import termios
5
+ import struct
6
+ import fcntl
7
+ import threading
8
+ import subprocess
9
+
10
+ class TerminalSession:
11
+ def __init__(self, session_id):
12
+ self.session_id = session_id
13
+ self.workspace = f"/tmp/workspaces/{session_id}"
14
+ os.makedirs(self.workspace, exist_ok=True)
15
+ self.master_fd, self.slave_fd = pty.openpty()
16
+ self.process = subprocess.Popen(
17
+ ["/bin/bash"],
18
+ preexec_fn=os.setsid,
19
+ stdin=self.slave_fd,
20
+ stdout=self.slave_fd,
21
+ stderr=self.slave_fd,
22
+ cwd=self.workspace,
23
+ universal_newlines=True,
24
+ )
25
+ self.websockets = []
26
+ self.running = True
27
+ self.thread = threading.Thread(target=self._read_output)
28
+ self.thread.daemon = True
29
+ self.thread.start()
30
+
31
+ def _read_output(self):
32
+ while self.running:
33
+ try:
34
+ r, _, _ = select.select([self.master_fd], [], [], 0.1)
35
+ if self.master_fd in r:
36
+ data = os.read(self.master_fd, 1024)
37
+ if data:
38
+ for ws in self.websockets:
39
+ asyncio.run_coroutine_threadsafe(ws.send_text(data.decode()), asyncio.get_event_loop())
40
+ except (OSError, ValueError):
41
+ break
42
+
43
+ def write(self, data):
44
+ os.write(self.master_fd, data.encode())
45
+
46
+ def add_websocket(self, ws):
47
+ self.websockets.append(ws)
48
+
49
+ def remove_websocket(self, ws):
50
+ self.websockets.remove(ws)
51
+
52
+ def close(self):
53
+ self.running = False
54
+ self.process.terminate()
55
+ os.close(self.master_fd)
56
+ os.close(self.slave_fd)
57
+
58
+ class TerminalManager:
59
+ def __init__(self):
60
+ self.sessions = {}
61
+
62
+ def get_session(self, session_id):
63
+ if session_id not in self.sessions:
64
+ self.sessions[session_id] = TerminalSession(session_id)
65
+ return self.sessions[session_id]