From 5e4e70309703d8e1831e055736f572980c83a42f Mon Sep 17 00:00:00 2001 From: Don Kingdon Date: Mon, 13 Apr 2026 03:21:43 +0000 Subject: [PATCH] Add DB-backed device file browser page --- PROJECT_STATE.md | 16 ++-- README.md | 6 ++ VERSION | 2 +- app/main/routes.py | 54 ++++++++++++++ app/templates/cloud/dashboard.html | 7 +- app/templates/cloud/device_files.html | 101 ++++++++++++++++++++++++++ 6 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 app/templates/cloud/device_files.html diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index e7da184..e22fa7d 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -4,7 +4,7 @@ OTB Cloud ## Current version -v0.2.0 +v0.2.1 ## Build date 2026-04-12 @@ -50,14 +50,15 @@ Portal-authenticated secure backup and storage platform for customer files, incl - Add Device flow - Remove Device flow for empty devices - Browser upload flow to device originals +- Device file browser page ## Immediate next tasks -1. Build first file library page -2. Add uploaded file listing per device -3. Add upload audit log UI or admin reference +1. Add single-file download +2. Add searchable file listing +3. Add rename basename-only flow 4. Add zip export flow -5. Add searchable file listing -6. Add media processing jobs +5. Add media processing jobs +6. Add derived/original filtering ## Notes Original uploaded files should remain preserved and effectively read-only. @@ -65,4 +66,5 @@ Any user-facing edits or processing outputs should create derivative files. Admin access should require owner-issued one-time support authorization. New tenants no longer receive default devices automatically; devices are now user-created. Devices can only be removed when no files are associated with them. -Browser uploads now write original files into device-specific originals directories and create DB records. +Browser uploads write original files into device-specific originals directories and create DB records. +The device browser is DB-backed and tenant-scoped. diff --git a/README.md b/README.md index 4930ce0..1951d56 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # OTB Cloud +## v0.2.1 - 2026-04-12 +- Added device file browser page +- Added Browse Files action per device +- File browser lists DB-backed files by device and tenant +- Added file count and device summary on browser page + ## v0.2.0 - 2026-04-12 - Added first browser upload flow for user-created devices - Added Upload Files action per device diff --git a/VERSION b/VERSION index 1474d00..22c08f7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.2.0 +v0.2.1 diff --git a/app/main/routes.py b/app/main/routes.py index 143c0fe..67b4a28 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -329,3 +329,57 @@ def upload_files(device_id: int): flash(f"Uploaded {uploaded_count} file(s) to {device['device_name']}.", "success") return redirect(url_for("main.dashboard")) + +@bp.route("/devices//files", methods=["GET"]) +@portal_session_required +def browse_device_files(device_id: int): + db = get_db() + + with db.cursor() as cur: + cur.execute( + """ + SELECT id, device_name, device_type, relative_path + FROM devices + WHERE id = %s AND tenant_id = %s + """, + (device_id, session["otb_tenant_id"]), + ) + device = cur.fetchone() + + if not device: + flash("Device not found.", "warning") + return redirect(url_for("main.dashboard")) + + cur.execute( + """ + SELECT + id, + file_kind, + relative_path, + directory_path, + original_filename, + basename, + extension, + mime_type, + size_bytes, + sha256, + uploaded_at, + is_immutable + FROM files + WHERE tenant_id = %s + AND device_id = %s + AND is_deleted = 0 + ORDER BY uploaded_at DESC, id DESC + """, + (session["otb_tenant_id"], device_id), + ) + files = cur.fetchall() + + return render_template( + "cloud/device_files.html", + user_email=session.get("otb_email"), + tenant_slug=session.get("otb_tenant_slug"), + device=device, + files=files, + file_count=len(files), + ) diff --git a/app/templates/cloud/dashboard.html b/app/templates/cloud/dashboard.html index a2bf9f5..8319bcc 100644 --- a/app/templates/cloud/dashboard.html +++ b/app/templates/cloud/dashboard.html @@ -62,6 +62,7 @@ {{ device.relative_path }}
Upload Files + Browse Files
@@ -76,7 +77,7 @@

Current scope

-

OTB Cloud now supports browser uploads to device originals.

+

OTB Cloud now supports browser uploads and device file browsing.

Live @@ -85,7 +86,7 @@

- Next steps are uploaded file listing, searchable library pages, zip export, and media processing jobs. + Next steps are single-file download, searchable library pages, zip export, and media processing jobs.

@@ -121,7 +122,7 @@

- After adding a device, you can upload one or more files into that device’s originals storage. + After adding a device, you can upload one or more files into that device’s originals storage and browse them here.

diff --git a/app/templates/cloud/device_files.html b/app/templates/cloud/device_files.html new file mode 100644 index 0000000..29714c1 --- /dev/null +++ b/app/templates/cloud/device_files.html @@ -0,0 +1,101 @@ +{% extends "portal_base.html" %} + +{% block title %}Device Files - OTB Cloud{% endblock %} + +{% block portal_content %} +
+
+

Device Files

+

{{ user_email }}

+

+ Browsing files for {{ device.device_name }} ({{ device.device_type }}). +

+
+ +
+ +
+
File count: {{ file_count }}
+
Device path: {{ device.relative_path }}
+
+
+
+ +{% if files %} +
+
+
+
+

Files

+

Files recorded in the database for this device.

+
+
+ DB-backed +
+
+ +
+ + + + + + + + + + + + {% for file in files %} + + + + + + + + {% endfor %} + +
NameKindSizeUploadedPath
+ {{ file.original_filename }}
+ + SHA256: {{ file.sha256 }} + +
+ {{ file.file_kind }} + {% if file.is_immutable %} +
immutable + {% endif %} +
+ {{ "{:,}".format(file.size_bytes or 0) }} bytes + + {{ file.uploaded_at }} + + {{ file.relative_path }} +
+
+
+
+{% else %} +
+
+
+
+

No files yet

+

This device does not have any uploaded files recorded yet.

+
+
+ Empty +
+
+ + +
+
+{% endif %} +{% endblock %}