Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
84185e1
feat: retry failed downloads with backoff, then remove them
khaosdoctor Nov 8, 2026
cba691e
fix(ui): wrap long file names in the downloads table
khaosdoctor Nov 8, 2026
69b5e36
feat(ui): add bulk retry, cancel and delete to the downloads table
khaosdoctor Nov 8, 2026
e48df7b
feat(ui): show the version in the sidebar and link the repository in …
khaosdoctor Nov 8, 2026
0039385
fix(ui): show the release version instead of the package.json one
khaosdoctor Nov 8, 2026
033e03c
feat: stream live download progress, speed and ETA
khaosdoctor Nov 8, 2026
bf652ca
feat: track uploads to peers and show them in the UI
khaosdoctor Nov 8, 2026
1bb8b73
perf: serve uploads zero-copy and take bytes and duration from a peer…
khaosdoctor Nov 8, 2026
d0f2b89
fix: address review feedback on failed-download cleanup and upload tr…
khaosdoctor Nov 8, 2026
b81d506
fix: stop the reaper when switched off mid-pass, contain upload track…
khaosdoctor Nov 8, 2026
75ef320
fix: coalesce every change source into one snapshot per second per st…
khaosdoctor Nov 8, 2026
0f0f954
fix: subscribe the overview stream to connectors through their object
khaosdoctor Nov 8, 2026
1f98365
fix: build the overview from SQL totals and cap receipts at the reque…
khaosdoctor Nov 9, 2026
3d5f676
fix: accept an upload receipt only for exactly the requested bytes
khaosdoctor Nov 9, 2026
785a9ff
refactor: track uploads as idempotent peer transfers and derive downl…
khaosdoctor Nov 9, 2026
ffca03e
fix: build the transfer URL locally, clear stale download speeds, rec…
khaosdoctor Nov 10, 2026
55f3b03
fix: keep the original uploads migration and convert the table in a n…
khaosdoctor Nov 10, 2026
c62369f
fix(db): wait on a busy SQLite lock instead of failing
khaosdoctor Nov 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ jobs:
push: true
tags: ${{ steps.meta-ui.outputs.tags }}
labels: ${{ steps.meta-ui.outputs.labels }}
# Shown in the UI. Same value as the image's org.opencontainers.image.version
# label: `1.2.3` on a v* tag, `main` or `pr-<n>` otherwise.
build-args: |
APP_VERSION=${{ steps.meta-ui.outputs.version }}
cache-from: type=gha,scope=ui
cache-to: type=gha,mode=max,scope=ui

Expand Down
21 changes: 21 additions & 0 deletions apps/backend/drizzle/0014_uploads.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CREATE TABLE `uploads` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`peer_name` text NOT NULL,
`peer_key_id` integer,
`item_id` text NOT NULL,
`filename` text NOT NULL,
`total_bytes` integer NOT NULL,
`range_start` integer DEFAULT 0 NOT NULL,
`requested_bytes` integer NOT NULL,
`sent_bytes` integer DEFAULT 0 NOT NULL,
`duration_ms` integer,
`status` text NOT NULL,
`error` text,
`started_at` text NOT NULL,
`updated_at` text NOT NULL,
`finished_at` text,
CONSTRAINT "uploads_status_check" CHECK("uploads"."status" in ('uploading', 'completed', 'aborted', 'unconfirmed'))
);
--> statement-breakpoint
CREATE INDEX `uploads_status_idx` ON `uploads` (`status`);--> statement-breakpoint
CREATE INDEX `uploads_started_at_idx` ON `uploads` (`started_at`);
29 changes: 29 additions & 0 deletions apps/backend/drizzle/0015_upload_transfers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
PRAGMA foreign_keys=OFF;--> statement-breakpoint
CREATE TABLE `__new_uploads` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`transfer_id` text NOT NULL,
`idempotency_key` text NOT NULL,
`peer_name` text NOT NULL,
`peer_key_id` integer,
`item_id` text NOT NULL,
`filename` text NOT NULL,
`total_bytes` integer NOT NULL,
`range_start` integer,
`sent_bytes` integer DEFAULT 0 NOT NULL,
`duration_ms` integer,
`status` text NOT NULL,
`started_at` text NOT NULL,
`updated_at` text NOT NULL,
`finished_at` text,
CONSTRAINT "uploads_status_check" CHECK("__new_uploads"."status" in ('uploading', 'completed', 'unconfirmed'))
);
--> statement-breakpoint
INSERT INTO `__new_uploads`("id", "transfer_id", "idempotency_key", "peer_name", "peer_key_id", "item_id", "filename", "total_bytes", "range_start", "sent_bytes", "duration_ms", "status", "started_at", "updated_at", "finished_at") SELECT "id", lower(hex(randomblob(16))), 'legacy:' || "id", "peer_name", "peer_key_id", "item_id", "filename", "total_bytes", "range_start", "sent_bytes", "duration_ms", CASE WHEN "status" = 'aborted' THEN 'unconfirmed' ELSE "status" END, "started_at", "updated_at", "finished_at" FROM `uploads`;--> statement-breakpoint
DROP TABLE `uploads`;--> statement-breakpoint
ALTER TABLE `__new_uploads` RENAME TO `uploads`;--> statement-breakpoint
PRAGMA foreign_keys=ON;--> statement-breakpoint
CREATE UNIQUE INDEX `uploads_transfer_id_unique` ON `uploads` (`transfer_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `uploads_idempotency_key_unique` ON `uploads` (`idempotency_key`);--> statement-breakpoint
CREATE INDEX `uploads_status_idx` ON `uploads` (`status`);--> statement-breakpoint
CREATE INDEX `uploads_started_at_idx` ON `uploads` (`started_at`);--> statement-breakpoint
ALTER TABLE `downloads` ADD `speed_bps` integer;
Loading