Console Files upload page -> shared-media (visible in GIMP); GIMP /media rw
This commit is contained in:
@@ -72,4 +72,8 @@ ssh sam@192.168.20.13 'cd /home/sam/Docker/Containers/dsh && docker compose buil
|
||||
pi host (see `deploy/pi-dash-sync.sh`; run it on a cron to keep it fresh).
|
||||
- Kids' accounts get a reduced view (no AI/chat, no admin-only items).
|
||||
|
||||
See `docs/` for the website inventory, the audio plan, and the dsh plugin plan.
|
||||
See `docs/` for the website inventory, the audio plan, and the dsh plugin plan.
|
||||
## How to get files into the tools (e.g. GIMP)
|
||||
Upload from your browser via the console → **Files** (top nav). Files appear in
|
||||
GIMP (and video/audio) under `/media/<your-username>/`. GIMP runs on `.13`, so
|
||||
use the upload page rather than a local file path.
|
||||
|
||||
@@ -89,6 +89,7 @@ services:
|
||||
PI_DASHBOARD_DIR: /pi-dashboard
|
||||
volumes:
|
||||
- /mnt/data/family-home-lab/pi-dashboard:/pi-dashboard:ro
|
||||
- /mnt/data/family-home-lab/shared-media:/shared-media:rw
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
@@ -185,7 +186,7 @@ services:
|
||||
- "8087:3000"
|
||||
volumes:
|
||||
- gimp-config:/config
|
||||
- /mnt/data/family-home-lab/shared-media:/media:ro
|
||||
- /mnt/data/family-home-lab/shared-media:/media:rw
|
||||
- /home/sam:/home/sam:ro
|
||||
- /home/sam/Downloads:/home/abc/Downloads
|
||||
- /home/sam/Pictures:/home/abc/Pictures
|
||||
|
||||
@@ -223,6 +223,67 @@ async def tool_embed(request: Request, tool_id: str) -> HTMLResponse:
|
||||
return templates.TemplateResponse(request, "embed.html", ctx)
|
||||
|
||||
|
||||
|
||||
SHARED_DIR = Path(os.getenv("SHARED_DIR", "/shared-media"))
|
||||
|
||||
|
||||
def _shared_files(username: str) -> list:
|
||||
"""List this user's uploaded files in the shared-media folder (created on demand)."""
|
||||
if not SHARED_DIR.is_dir():
|
||||
return []
|
||||
user_dir = SHARED_DIR / username
|
||||
if not user_dir.is_dir():
|
||||
return []
|
||||
out = []
|
||||
for f in sorted(user_dir.iterdir()):
|
||||
if f.is_file():
|
||||
out.append({"name": f.name, "size": f.stat().st_size})
|
||||
return out
|
||||
|
||||
|
||||
@app.get("/files", response_class=HTMLResponse)
|
||||
async def files_page(request: Request) -> HTMLResponse:
|
||||
user = await _current_user(request)
|
||||
if user is None:
|
||||
return RedirectResponse("/login", status_code=303)
|
||||
return templates.TemplateResponse(
|
||||
request, "files.html", {"user": user, "files": _shared_files(user.username)}
|
||||
)
|
||||
|
||||
|
||||
@app.post("/files/upload")
|
||||
async def files_upload(request: Request, file: Annotated[UploadFile, File()]) -> HTMLResponse:
|
||||
user = await _current_user(request)
|
||||
if user is None:
|
||||
return RedirectResponse("/login", status_code=303)
|
||||
error = None
|
||||
if not file or not file.filename:
|
||||
error = "No file chosen."
|
||||
else:
|
||||
try:
|
||||
name = Path(file.filename).name.lower()
|
||||
# allow images + common docs (photos are the main use case)
|
||||
if not name.endswith((".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg", ".psd", ".xcf", ".pdf", ".txt", ".md")):
|
||||
error = f"File type not allowed: {file.filename}"
|
||||
else:
|
||||
SHARED_DIR.mkdir(exist_ok=True)
|
||||
user_dir = SHARED_DIR / user.username
|
||||
user_dir.mkdir(exist_ok=True)
|
||||
data = await file.read()
|
||||
if len(data) > 50 * 1024 * 1024:
|
||||
error = "File too large (max 50 MB)."
|
||||
else:
|
||||
(user_dir / file.filename).write_bytes(data)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.warning("files upload failed", exc_info=True)
|
||||
error = f"Upload failed: {exc}"
|
||||
return templates.TemplateResponse(
|
||||
request, "files.html",
|
||||
{"user": user, "files": _shared_files(user.username),
|
||||
"error": error, "ok": error is None and file and file.filename},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/admin", response_class=HTMLResponse)
|
||||
async def admin_panel(request: Request) -> HTMLResponse:
|
||||
user = await _current_user(request)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<a class="nav-wordmark" href="/">Home Lab</a>
|
||||
<div class="nav-right">
|
||||
<a href="/transcriber" class="button-utility">Transcriber</a>
|
||||
<a href="/files" class="button-utility">Files</a>
|
||||
{% if user.is_admin %}
|
||||
<a href="/admin" class="button-utility">Admin</a>
|
||||
{% endif %}
|
||||
|
||||
42
portal/templates/files.html
Normal file
42
portal/templates/files.html
Normal file
@@ -0,0 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Files — {{ settings.APP_NAME }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page" style="max-width:760px">
|
||||
<h1 class="greeting">My files</h1>
|
||||
<p class="muted">Upload pictures here from your computer — they instantly appear in the <strong>GIMP</strong> (and video/audio) apps, under the shared <code>/media</code> folder, in your own <code>{{ user.username }}/</code> subfolder.</p>
|
||||
|
||||
{% if ok %}<div class="form-success" style="background:#e8f7ec;color:#1aae39;border:1px solid #b8e6c7;padding:10px 12px;border-radius:8px">✅ Uploaded. It's now in GIMP under <code>{{ user.username }}/…</code></div>{% endif %}
|
||||
{% if error %}<div class="form-error">{{ error }}</div>{% endif %}
|
||||
|
||||
<div class="admin-panel">
|
||||
<div class="linkout-card stack">
|
||||
<h2 class="section-title">Upload a picture</h2>
|
||||
<form method="post" action="/files/upload" enctype="multipart/form-data">
|
||||
<div class="form-field">
|
||||
<input class="text-input" type="file" name="file" accept="image/*,.pdf,.txt,.md" required>
|
||||
</div>
|
||||
<button class="button button-primary width-full" type="submit">Upload</button>
|
||||
</form>
|
||||
<p class="faint margin-top" style="font: var(--type-caption)">
|
||||
On your computer (e.g. <code>.27</code>) uploads go to the lab's shared folder on <code>.13</code>, which GIMP opens at <code>/media/{{ user.username }}/</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section class="stack">
|
||||
<h2 class="section-title">Uploaded ({{ files|length }})</h2>
|
||||
{% if files %}
|
||||
<ul class="file-list">
|
||||
{% for f in files %}
|
||||
<li><span class="file-name" title="{{ f.name }}">{{ f.name }}</span><span class="faint">{{ (f.size/1024)|round(1) }} KB</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p class="faint margin-top" style="font: var(--type-caption)">
|
||||
To open one in GIMP: go to <code>gimp.lab.audasmedia.com.au</code> → <strong>File → Open</strong> → <code>/media/{{ user.username }}/…</code>.
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="faint">Nothing uploaded yet.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user