瀏覽代碼

changing strategy in order to make the updates possible

Nicole 1 月之前
父節點
當前提交
421903ea5c

+ 11 - 0
overlay_manager/Dockerfile

@@ -0,0 +1,11 @@
+FROM python:3.10-slim
+
+WORKDIR /app
+
+# Install FastAPI and dependencies
+RUN pip install --no-cache-dir fastapi uvicorn jinja2 python-multipart
+
+COPY . .
+
+# Run the app on port 80
+CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

+ 69 - 0
overlay_manager/main.py

@@ -0,0 +1,69 @@
+import os
+import shutil
+from fastapi import FastAPI, Request, File, UploadFile, Form
+from fastapi.responses import HTMLResponse, RedirectResponse
+from fastapi.templating import Jinja2Templates
+from fastapi.staticfiles import StaticFiles
+
+# =====================================================================
+# MODIFICATION: ROOT PATH FOR NGINX
+# Why: When Nginx routes traffic from yourdomain.com/patch-manager/ 
+# to this sidecar, FastAPI needs to know its "base" URL is no longer "/".
+# If we don't set this, the app will try to load CSS and submit forms 
+# to the wrong root URL, resulting in 404 errors.
+# =====================================================================
+app = FastAPI(root_path="/patch-manager")
+
+app.mount("/static", StaticFiles(directory="static"), name="static")
+templates = Jinja2Templates(directory="templates")
+
+OVERLAY_DIR = "/srv"
+
+@app.get("/", response_class=HTMLResponse)
+async def index(request: Request):
+    files = []
+    if os.path.exists(OVERLAY_DIR):
+        for root, dirs, filenames in os.walk(OVERLAY_DIR):
+            for filename in filenames:
+                rel_path = os.path.relpath(os.path.join(root, filename), OVERLAY_DIR)
+                files.append(rel_path)
+    return templates.TemplateResponse("index.html", {"request": request, "files": sorted(files)})
+
+@app.post("/upload")
+async def upload_file(file: UploadFile = File(...), target_path: str = Form("")):
+    save_dir = os.path.join(OVERLAY_DIR, target_path.strip("/"))
+    os.makedirs(save_dir, exist_ok=True)
+    file_location = os.path.join(save_dir, file.filename)
+    with open(file_location, "wb+") as file_object:
+        shutil.copyfileobj(file.file, file_object)
+    
+    # Notice we redirect to "/" here. Because we set root_path="/patch-manager",
+    # FastAPI automatically translates this redirect to "/patch-manager/"!
+    return RedirectResponse(url="/", status_code=303)
+
+@app.post("/delete")
+async def delete_file(filepath: str = Form(...)):
+    target = os.path.join(OVERLAY_DIR, filepath)
+    if os.path.exists(target):
+        os.remove(target)
+        target_dir = os.path.dirname(target)
+        if not os.listdir(target_dir) and target_dir != OVERLAY_DIR:
+            os.rmdir(target_dir)
+    return RedirectResponse(url="/", status_code=303)
+
+@app.get("/edit", response_class=HTMLResponse)
+async def edit_file(request: Request, filepath: str):
+    target = os.path.join(OVERLAY_DIR, filepath)
+    content = ""
+    if os.path.exists(target):
+        with open(target, "r", encoding="utf-8", errors="ignore") as f:
+            content = f.read()
+    return templates.TemplateResponse("edit.html", {"request": request, "filepath": filepath, "content": content})
+
+@app.post("/edit")
+async def save_file(filepath: str = Form(...), content: str = Form(...)):
+    target = os.path.join(OVERLAY_DIR, filepath)
+    if os.path.exists(target):
+        with open(target, "w", encoding="utf-8") as f:
+            f.write(content)
+    return RedirectResponse(url="/", status_code=303)

二進制
overlay_manager/static/logo.png


+ 36 - 0
overlay_manager/templates/edit.html

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Edit - {{ filepath }}</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
+</head>
+<body class="bg-light">
+    <nav class="navbar navbar-dark shadow-sm py-2 mb-4" style="background-color: #0B61A4;">
+        <div class="container">
+            <span class="navbar-brand mb-0 h4"><i class="bi bi-pencil-square me-2"></i>Overlay Editor</span>
+            <a href="/" class="btn btn-outline-light btn-sm"><i class="bi bi-arrow-left me-1"></i>Back to Manager</a>
+        </div>
+    </nav>
+
+    <div class="container">
+        <div class="card shadow border-0 rounded-3">
+            <div class="card-header bg-white pt-3 pb-2 d-flex justify-content-between align-items-center">
+                <span class="font-monospace fw-bold text-primary">{{ filepath }}</span>
+            </div>
+            <div class="card-body p-0">
+                <form action="/edit" method="post">
+                    <input type="hidden" name="filepath" value="{{ filepath }}">
+                    <textarea name="content" class="form-control border-0 font-monospace text-bg-dark rounded-0 p-3" rows="25" style="resize: none;" spellcheck="false">{{ content }}</textarea>
+                    
+                    <div class="p-3 bg-light text-end rounded-bottom-3">
+                        <a href="/" class="btn btn-secondary me-2">Cancel</a>
+                        <button type="submit" class="btn btn-success fw-bold"><i class="bi bi-save2-fill me-2"></i>Commit Changes</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

+ 56 - 0
overlay_manager/templates/index.html

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>ArduPilot Overlay Manager</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
+    <style>
+        .btn-custom { background-color: #0B61A4; color: white; transition: all 0.2s; }
+        .btn-custom:hover { background-color: #084b82; color: white; transform: translateY(-1px); }
+    </style>
+</head>
+<body class="bg-light">
+    <nav class="navbar navbar-dark shadow-sm py-3" style="background-color: #0B61A4;">
+        <div class="container d-flex align-items-center">
+            <img src="/static/logo.png" alt="ArduPilot" height="40" class="me-3 bg-white p-1 rounded">
+            <span class="navbar-brand mb-0 h3 fw-bold">Overlay Manager</span>
+        </div>
+    </nav>
+
+    <div class="container mt-4">
+        <div class="card shadow border-0 rounded-3 mb-4">
+            <div class="card-body p-4">
+                <h5 class="card-title fw-bold text-secondary mb-3"><i class="bi bi-cloud-arrow-up-fill me-2 text-primary"></i>Inject Custom Source</h5>
+                <form action="/upload" method="post" enctype="multipart/form-data" class="row g-3">
+                    <div class="col-md-5"><input class="form-control border-primary" type="file" name="file" required></div>
+                    <div class="col-md-5"><input type="text" class="form-control border-primary" name="target_path" placeholder="Path (e.g., libraries/AP_HAL)"></div>
+                    <div class="col-md-2"><button type="submit" class="btn btn-custom w-100 fw-bold">Upload</button></div>
+                </form>
+            </div>
+        </div>
+
+        <div class="card shadow border-0 rounded-3">
+            <div class="card-body p-0">
+                <table class="table table-hover align-middle mb-0">
+                    <thead class="table-light"><tr><th class="ps-4 py-3">Source Path</th><th class="text-end pe-4 py-3">Actions</th></tr></thead>
+                    <tbody>
+                        {% for file in files %}
+                        <tr>
+                            <td class="ps-4 font-monospace">{{ file }}</td>
+                            <td class="text-end pe-4">
+                                <a href="/edit?filepath={{ file }}" class="btn btn-sm btn-outline-primary me-2"><i class="bi bi-pencil"></i> Edit</a>
+                                <form action="/delete" method="post" class="d-inline" onsubmit="return confirm('Delete this file?');">
+                                    <input type="hidden" name="filepath" value="{{ file }}">
+                                    <button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i> Delete</button>
+                                </form>
+                            </td>
+                        </tr>
+                        {% endfor %}
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</body>
+</html>