| |
| FROM ubuntu:22.04 |
|
|
| ENV DEBIAN_FRONTEND=noninteractive |
| ENV HOSTNAME=Ubuntu |
| ENV DISPLAY=:99 |
|
|
| |
| RUN apt-get update && apt-get install -y --no-install-recommends \ |
| ca-certificates \ |
| curl \ |
| wget \ |
| git \ |
| sudo \ |
| docker.io \ |
| htop \ |
| btop \ |
| neovim \ |
| lsof \ |
| python3 \ |
| python3-pip \ |
| nginx \ |
| supervisor \ |
| gnupg \ |
| xvfb \ |
| x11vnc \ |
| fluxbox \ |
| novnc \ |
| websockify \ |
| dbus-x11 \ |
| libnss3 \ |
| libatk1.0-0 \ |
| libatk-bridge2.0-0 \ |
| libcups2 \ |
| libdrm2 \ |
| libxkbcommon0 \ |
| libxcomposite1 \ |
| libxdamage1 \ |
| libxfixes3 \ |
| libxrandr2 \ |
| libgbm1 \ |
| libpango-1.0-0 \ |
| libcairo2 \ |
| libasound2 \ |
| libgtk-3-0 \ |
| libx11-xcb1 \ |
| libxss1 \ |
| && rm -rf /var/lib/apt/lists/* |
|
|
| |
| RUN ln -s /usr/share/novnc/vnc.html /usr/share/novnc/index.html |
|
|
| |
| RUN wget -qO antigravity.deb "https://antigravity.google/download/linux?format=deb" && \ |
| apt-get update && apt-get install -y ./antigravity.deb && \ |
| rm antigravity.deb && \ |
| rm -rf /var/lib/apt/lists/* |
|
|
| |
| RUN pip3 install fastapi uvicorn pydantic |
|
|
| |
| RUN mkdir -p /opt/api /workspace /var/log/supervisor /root/.vnc |
|
|
| |
| COPY <<"EOF" /opt/api/api.py |
| from fastapi import FastAPI, HTTPException |
| from pydantic import BaseModel |
| import os, subprocess, shutil |
|
|
| app = FastAPI() |
|
|
| class WriteRequest(BaseModel): |
| path: str |
| content: str |
|
|
| class ExecRequest(BaseModel): |
| command: str |
|
|
| class DeleteRequest(BaseModel): |
| path: str |
|
|
| @app.get('/api/list') |
| def list_dir(path: str = '/workspace'): |
| try: |
| return {'files': os.listdir(path)} |
| except Exception as e: |
| raise HTTPException(status_code=400, detail=str(e)) |
|
|
| @app.get('/api/read') |
| def read_file(path: str): |
| try: |
| with open(path, 'r', encoding='utf-8') as f: |
| return {'content': f.read()} |
| except Exception as e: |
| raise HTTPException(status_code=400, detail=str(e)) |
|
|
| @app.post('/api/write') |
| def write_file(req: WriteRequest): |
| try: |
| os.makedirs(os.path.dirname(req.path), exist_ok=True) |
| with open(req.path, 'w', encoding='utf-8') as f: |
| f.write(req.content) |
| return {'status': 'success'} |
| except Exception as e: |
| raise HTTPException(status_code=400, detail=str(e)) |
|
|
| @app.post('/api/exec') |
| def exec_cmd(req: ExecRequest): |
| try: |
| result = subprocess.run(req.command, shell=True, capture_output=True, text=True) |
| return {'stdout': result.stdout, 'stderr': result.stderr, 'returncode': result.returncode} |
| except Exception as e: |
| raise HTTPException(status_code=400, detail=str(e)) |
|
|
| @app.post('/api/delete') |
| def delete_item(req: DeleteRequest): |
| try: |
| if os.path.isdir(req.path): |
| shutil.rmtree(req.path) |
| else: |
| os.remove(req.path) |
| return {'status': 'success'} |
| except Exception as e: |
| raise HTTPException(status_code=400, detail=str(e)) |
| EOF |
|
|
| |
| COPY <<"EOF" /etc/nginx/nginx.conf |
| events {} |
| http { |
| server { |
| listen 7860; |
| |
| |
| location /api/ { |
| proxy_pass http://127.0.0.1:8000; |
| proxy_set_header Host $host; |
| proxy_set_header X-Real-IP $remote_addr; |
| } |
| |
| |
| location / { |
| proxy_pass http://127.0.0.1:6080/; |
| proxy_set_header Host $host; |
| proxy_set_header Upgrade $http_upgrade; |
| proxy_set_header Connection upgrade; |
| proxy_set_header Accept-Encoding gzip; |
| } |
| } |
| } |
| EOF |
|
|
| |
| COPY <<"EOF" /etc/supervisor/conf.d/supervisord.conf[supervisord] |
| nodaemon=true |
|
|
| [program:xvfb] |
| command=Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset |
| priority=10 |
| autorestart=true |
|
|
| [program:fluxbox] |
| command=fluxbox |
| environment=DISPLAY=":99" |
| priority=20 |
| autorestart=true[program:x11vnc] |
| command=x11vnc -display :99 -nopw -forever -shared -bg -xkb |
| priority=30 |
| autorestart=true |
|
|
| [program:novnc] |
| command=websockify --web /usr/share/novnc 6080 localhost:5900 |
| priority=40 |
| autorestart=true |
|
|
| [program:antigravity] |
| command=antigravity --no-sandbox --disable-dev-shm-usage |
| environment=DISPLAY=":99" |
| directory=/workspace |
| priority=50 |
| autorestart=true |
|
|
| [program:api] |
| command=uvicorn api:app --host 127.0.0.1 --port 8000 |
| directory=/opt/api |
| priority=60 |
| autorestart=true |
|
|
| [program:nginx] |
| command=nginx -g "daemon off;" |
| priority=70 |
| autorestart=true |
| EOF |
|
|
| |
| WORKDIR /workspace |
|
|
| EXPOSE 7860 |
|
|
| |
| CMD["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] |