| 375 | } |
| 376 | |
| 377 | std::string CmdListBlobs(const std::string& provider, const std::string& accountId, const std::string& appId) { |
| 378 | std::string tokenPath = GetTokenPath(provider); |
| 379 | if (tokenPath.empty()) { |
| 380 | return JsonError("Cannot determine config directory"); |
| 381 | } |
| 382 | |
| 383 | auto prov = CreateCloudProvider(provider); |
| 384 | if (!prov) { |
| 385 | return JsonError("Unknown provider: " + provider); |
| 386 | } |
| 387 | |
| 388 | if (!prov->Init(tokenPath)) { |
| 389 | return JsonError("Failed to initialize provider"); |
| 390 | } |
| 391 | |
| 392 | if (!prov->IsAuthenticated()) { |
| 393 | prov->Shutdown(); |
| 394 | return JsonError("Not authenticated"); |
| 395 | } |
| 396 | |
| 397 | // List files under accountId/appId/blobs/ while preserving the provider's |
| 398 | // verified-complete flag. Orphan pruning must refuse partial listings. |
| 399 | std::string prefix = accountId + "/" + appId + "/blobs/"; |
| 400 | std::vector<ICloudProvider::FileInfo> files; |
| 401 | bool complete = false; |
| 402 | if (!prov->ListChecked(prefix, files, &complete)) { |
| 403 | prov->Shutdown(); |
| 404 | return JsonObject({ |
| 405 | {"success", JsonBool(false)}, |
| 406 | {"complete", JsonBool(false)}, |
| 407 | {"error", JsonString("Failed to list blobs")}, |
| 408 | {"blobs", "[]"} |
| 409 | }); |
| 410 | } |
| 411 | prov->Shutdown(); |
| 412 | |
| 413 | // Extract just the blob filenames (not full paths) |
| 414 | std::ostringstream blobs; |
| 415 | blobs << "["; |
| 416 | bool first = true; |
| 417 | for (const auto& f : files) { |
| 418 | if (f.path.back() == '/') continue; |
| 419 | |
| 420 | std::string filename; |
| 421 | if (f.path.size() > prefix.size()) { |
| 422 | filename = f.path.substr(prefix.size()); |
| 423 | if (filename.find('/') != std::string::npos) continue; |
| 424 | } else { |
| 425 | continue; |
| 426 | } |
| 427 | |
| 428 | if (!first) blobs << ","; |
| 429 | blobs << JsonString(filename); |
| 430 | first = false; |
| 431 | } |
| 432 | blobs << "]"; |
| 433 | |
| 434 | return JsonObject({ |
no test coverage detected