Loading debian/changelog +12 −0 Original line number Diff line number Diff line mediadb (20260419+21) unstable; urgency=low * Fix delete_store in ClusterMediaBackend to properly delete all associated media blobs from cluster storage. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:15:00 +0200 mediadb (20260419+20) unstable; urgency=low * Make MDB import synchronous without background thread. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:00:00 +0200 mediadb (20260419+19) unstable; urgency=low * Add cluster-wide Scrub & Rebalance status to admin UI: Loading src/app.cpp +2 −28 Original line number Diff line number Diff line Loading @@ -1639,34 +1639,8 @@ HttpResponse App::handle_export(const HttpRequest& req) { HttpResponse App::handle_import(const HttpRequest& req) { if (!auth_.is_authorized(req)) return error_json(401, "unauthorized"); if (!auth_.check_gpo(req, GPO_IMPORT)) return error_json(403, "no permission: db.import"); if (req.body.empty()) return error_json(400, "empty body"); if (import_running_.load()) return error_json(409, "import already in progress"); // Copy body and run import in background thread auto data = std::make_shared<std::vector<uint8_t>>( reinterpret_cast<const uint8_t*>(req.body.data()), reinterpret_cast<const uint8_t*>(req.body.data()) + req.body.size()); { std::lock_guard<std::mutex> lk(import_mutex_); import_running_.store(true); import_ok_.store(false); import_error_.clear(); } if (import_thread_.joinable()) import_thread_.join(); import_thread_ = std::thread([this, data]() { bool ok = db_.import_db_from_buffer(data->data(), data->size()); std::lock_guard<std::mutex> lk(import_mutex_); import_ok_.store(ok); if (!ok) import_error_ = "import failed"; import_running_.store(false); }); json_object* j = json_object_new_object(); json_object_object_add(j, "status", json_object_new_string("accepted")); json_object_object_add(j, "message", json_object_new_string("import started, poll /api/import/status")); return HttpResponse::json(202, j); return start_import(reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size()); } // ---- login handler ---- Loading src/backend.cpp +19 −1 Original line number Diff line number Diff line Loading @@ -2467,9 +2467,27 @@ std::vector<StoreRecord> ClusterMediaBackend::list_stores() const { bool ClusterMediaBackend::delete_store(const std::string& id) { std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_); // Collect all media and albums to remove their cluster objects std::vector<std::string> media_to_remove; auto albums = local_.list_albums(id); for (const auto& album : albums) { auto media_list = local_.list_media(album.id); for (const auto& m : media_list) { media_to_remove.push_back(m.id); } } if (!local_.delete_store(id)) return false; // Remove store blob (contains all media data inline) // Remove store metadata blob cluster_.remove("store:" + id); // Remove all associated media data blobs for (const auto& mid : media_to_remove) { cluster_.remove("media:" + mid); } replicate_index(); return true; } Loading Loading
debian/changelog +12 −0 Original line number Diff line number Diff line mediadb (20260419+21) unstable; urgency=low * Fix delete_store in ClusterMediaBackend to properly delete all associated media blobs from cluster storage. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:15:00 +0200 mediadb (20260419+20) unstable; urgency=low * Make MDB import synchronous without background thread. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:00:00 +0200 mediadb (20260419+19) unstable; urgency=low * Add cluster-wide Scrub & Rebalance status to admin UI: Loading
src/app.cpp +2 −28 Original line number Diff line number Diff line Loading @@ -1639,34 +1639,8 @@ HttpResponse App::handle_export(const HttpRequest& req) { HttpResponse App::handle_import(const HttpRequest& req) { if (!auth_.is_authorized(req)) return error_json(401, "unauthorized"); if (!auth_.check_gpo(req, GPO_IMPORT)) return error_json(403, "no permission: db.import"); if (req.body.empty()) return error_json(400, "empty body"); if (import_running_.load()) return error_json(409, "import already in progress"); // Copy body and run import in background thread auto data = std::make_shared<std::vector<uint8_t>>( reinterpret_cast<const uint8_t*>(req.body.data()), reinterpret_cast<const uint8_t*>(req.body.data()) + req.body.size()); { std::lock_guard<std::mutex> lk(import_mutex_); import_running_.store(true); import_ok_.store(false); import_error_.clear(); } if (import_thread_.joinable()) import_thread_.join(); import_thread_ = std::thread([this, data]() { bool ok = db_.import_db_from_buffer(data->data(), data->size()); std::lock_guard<std::mutex> lk(import_mutex_); import_ok_.store(ok); if (!ok) import_error_ = "import failed"; import_running_.store(false); }); json_object* j = json_object_new_object(); json_object_object_add(j, "status", json_object_new_string("accepted")); json_object_object_add(j, "message", json_object_new_string("import started, poll /api/import/status")); return HttpResponse::json(202, j); return start_import(reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size()); } // ---- login handler ---- Loading
src/backend.cpp +19 −1 Original line number Diff line number Diff line Loading @@ -2467,9 +2467,27 @@ std::vector<StoreRecord> ClusterMediaBackend::list_stores() const { bool ClusterMediaBackend::delete_store(const std::string& id) { std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_); // Collect all media and albums to remove their cluster objects std::vector<std::string> media_to_remove; auto albums = local_.list_albums(id); for (const auto& album : albums) { auto media_list = local_.list_media(album.id); for (const auto& m : media_list) { media_to_remove.push_back(m.id); } } if (!local_.delete_store(id)) return false; // Remove store blob (contains all media data inline) // Remove store metadata blob cluster_.remove("store:" + id); // Remove all associated media data blobs for (const auto& mid : media_to_remove) { cluster_.remove("media:" + mid); } replicate_index(); return true; } Loading