daemon: download.add / download.list / download.get behind the store

VeloxDispatcher now takes a store::Db& and three handlers are real:

- download.list -> store::Tasks::list (filter / sort / paging in SQL) ->
  to_summary per row. No more empty-table stub.
- download.add -> resolve saveDir (spec, else saveTo.defaultDir; ~ expanded)
  and the leaf (spec.filename, else the URL's last segment percent-decoded,
  else download.bin) -> fs::resolve_target against canonicalize_root'd
  saveTo.allowedRoots. Any path-destination failure is -32011 with the
  *original* saveDir in data.path. On success a TaskRow is inserted in
  state `queued` (or `new` for startMode "manual") and {taskId, state}
  returned. The scheduler that would then admit it is D4.
- download.get -> store::Tasks::get; a real -32010 + data.taskId for an
  unknown id, else a TaskDetail (segmentDetail empty until the engine
  segments the task, which the schema permits).

util/time.hpp: now_iso() factored out of ws_server.cpp.

main.cpp constructs the dispatcher with the opened db. The three
integration tests build an in-memory migrated db for it; velox.client
now drives the full slice through the CLI — add outside roots -> -32011
with data.path, add into an allowed root -> a task that download.list
shows and download.get details, unknown id -> -32010. Verified with the
real binaries: velox add persists, velox ls shows it, it survives a
daemon restart, /etc is refused.

ASan+UBSan and TSan clean; 34 daemon/cli tests green. deferrals.md:
D2 down to just download.probe; D3 down to categories/queues/rules/
settings/limiter/schedule.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
This commit is contained in:
2026-09-10 19:58:45 +04:00
co-authored by Claude Sonnet 5
parent 7514f5a4c4
commit 3db0d01f9d
10 changed files with 257 additions and 42 deletions
+1 -9
View File
@@ -16,6 +16,7 @@
#include "rpc/event_loop.hpp"
#include "rpc/ws_handshake.hpp"
#include "util/time.hpp"
#include "store/pairings.hpp"
#include "store/sqlite.hpp"
#include "version.hpp"
@@ -32,15 +33,6 @@ std::error_code errc(int e) { return std::error_code(e, std::generic_category())
constexpr std::size_t kMaxOutBytes = 16 * 1024 * 1024;
constexpr std::size_t kMaxHandshakeBytes = 16 * 1024;
std::string now_iso() {
std::time_t t = std::time(nullptr);
std::tm tm{};
::gmtime_r(&t, &tm);
char buf[32];
std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm);
return std::string(buf);
}
std::string uuid4() {
std::random_device rd;
std::uniform_int_distribution<std::uint32_t> d;