|
9 | 9 | #include <unordered_set> |
10 | 10 |
|
11 | 11 | #include "metadata_store.h" |
12 | | -#include "notebook.h" |
| 12 | +#include "bundled_notebook.h" |
13 | 13 | #include "utils/file_utils.h" |
14 | 14 | #include "utils/logger.h" |
15 | 15 | #include "utils/utils.h" |
@@ -1870,10 +1870,6 @@ VxCoreError BundledFolderManager::ImportFolder(const std::string &dest_folder_pa |
1870 | 1870 | continue; |
1871 | 1871 | } |
1872 | 1872 | if (entry.is_directory()) { |
1873 | | - // Skip vx_notebook metadata folder |
1874 | | - if (entry_name == "vx_notebook") { |
1875 | | - continue; |
1876 | | - } |
1877 | 1873 | copy_filtered(entry.path(), dest / entry_name); |
1878 | 1874 | } else if (entry.is_regular_file()) { |
1879 | 1875 | // Apply suffix filter if allowlist is specified |
@@ -1923,14 +1919,15 @@ VxCoreError BundledFolderManager::ImportFolder(const std::string &dest_folder_pa |
1923 | 1919 | std::function<VxCoreError(const std::string &, FolderConfig &)> index_contents = |
1924 | 1920 | [&](const std::string &rel_path, FolderConfig &config) -> VxCoreError { |
1925 | 1921 | std::string abs_path = GetContentPath(rel_path); |
| 1922 | + const bool is_root = (rel_path.empty() || rel_path == "."); |
1926 | 1923 |
|
1927 | 1924 | try { |
1928 | 1925 | for (const auto &entry : fs::directory_iterator(abs_path)) { |
1929 | 1926 | std::string entry_name = entry.path().filename().string(); |
1930 | 1927 |
|
1931 | 1928 | if (entry.is_directory()) { |
1932 | | - // Skip hidden folders and vx_notebook metadata folder |
1933 | | - if (entry_name.empty() || entry_name[0] == '.' || entry_name == "vx_notebook") { |
| 1929 | + if (entry_name.empty() || entry_name[0] == '.' || |
| 1930 | + (is_root && entry_name == BundledNotebook::kMetadataFolderName)) { |
1934 | 1931 | continue; |
1935 | 1932 | } |
1936 | 1933 |
|
@@ -2230,4 +2227,97 @@ VxCoreError BundledFolderManager::UnindexNode(const std::string &node_path) { |
2230 | 2227 | VXCORE_LOG_WARN("UnindexNode: Node not found in metadata: %s", clean_path.c_str()); |
2231 | 2228 | return VXCORE_ERR_NOT_FOUND; |
2232 | 2229 | } |
| 2230 | + |
| 2231 | +VxCoreError BundledFolderManager::ListExternalNodes(const std::string &folder_path, |
| 2232 | + FolderContents &out_contents) { |
| 2233 | + const auto clean_path = GetCleanRelativePath(folder_path); |
| 2234 | + VXCORE_LOG_INFO("ListExternalNodes: path=%s", clean_path.c_str()); |
| 2235 | + |
| 2236 | + // Clear output |
| 2237 | + out_contents.files.clear(); |
| 2238 | + out_contents.folders.clear(); |
| 2239 | + |
| 2240 | + // Get folder config (indexed items) |
| 2241 | + FolderConfig *config = nullptr; |
| 2242 | + VxCoreError error = GetFolderConfig(clean_path, &config); |
| 2243 | + if (error != VXCORE_OK) { |
| 2244 | + VXCORE_LOG_ERROR("ListExternalNodes: Failed to get folder config: path=%s, error=%d", |
| 2245 | + clean_path.c_str(), error); |
| 2246 | + return error; |
| 2247 | + } |
| 2248 | + |
| 2249 | + // Build sets of indexed items for fast lookup |
| 2250 | + std::set<std::string> indexed_files; |
| 2251 | + std::set<std::string> indexed_folders; |
| 2252 | + for (const auto &file : config->files) { |
| 2253 | + indexed_files.insert(file.name); |
| 2254 | + } |
| 2255 | + for (const auto &folder_name : config->folders) { |
| 2256 | + indexed_folders.insert(folder_name); |
| 2257 | + } |
| 2258 | + |
| 2259 | + // Get filesystem content path |
| 2260 | + std::string content_path = GetContentPath(clean_path); |
| 2261 | + if (!fs::exists(content_path)) { |
| 2262 | + VXCORE_LOG_WARN("ListExternalNodes: Content path does not exist: %s", content_path.c_str()); |
| 2263 | + return VXCORE_ERR_NOT_FOUND; |
| 2264 | + } |
| 2265 | + |
| 2266 | + // Check if assets/attachments folders should be skipped (only if single name) |
| 2267 | + const NotebookConfig &nb_config = notebook_->GetConfig(); |
| 2268 | + const bool need_check_assets_folder = IsSingleName(nb_config.assets_folder); |
| 2269 | + const bool need_check_attachments_folder = IsSingleName(nb_config.attachments_folder); |
| 2270 | + |
| 2271 | + const bool is_root = clean_path.empty() || clean_path == "."; |
| 2272 | + |
| 2273 | + // Scan filesystem and find unindexed items |
| 2274 | + try { |
| 2275 | + for (const auto &entry : fs::directory_iterator(content_path)) { |
| 2276 | + std::string entry_name = entry.path().filename().string(); |
| 2277 | + |
| 2278 | + // Skip hidden files/folders (starting with '.') |
| 2279 | + if (entry_name.empty() || entry_name[0] == '.') { |
| 2280 | + continue; |
| 2281 | + } |
| 2282 | + |
| 2283 | + // Skip metadata folder (only at root) |
| 2284 | + if (is_root && entry_name == BundledNotebook::kMetadataFolderName) { |
| 2285 | + continue; |
| 2286 | + } |
| 2287 | + |
| 2288 | + // Skip assets and attachments folders |
| 2289 | + if ((need_check_assets_folder && entry_name == nb_config.assets_folder) || |
| 2290 | + (need_check_attachments_folder && entry_name == nb_config.attachments_folder)) { |
| 2291 | + continue; |
| 2292 | + } |
| 2293 | + |
| 2294 | + if (entry.is_directory()) { |
| 2295 | + // Check if folder is NOT indexed |
| 2296 | + if (indexed_folders.find(entry_name) == indexed_folders.end()) { |
| 2297 | + // External folder - create a minimal FolderRecord |
| 2298 | + FolderRecord record; |
| 2299 | + record.name = entry_name; |
| 2300 | + // No ID since it's not indexed |
| 2301 | + out_contents.folders.push_back(record); |
| 2302 | + } |
| 2303 | + } else if (entry.is_regular_file()) { |
| 2304 | + // Check if file is NOT indexed |
| 2305 | + if (indexed_files.find(entry_name) == indexed_files.end()) { |
| 2306 | + // External file - create a minimal FileRecord |
| 2307 | + FileRecord record; |
| 2308 | + record.name = entry_name; |
| 2309 | + // No ID since it's not indexed |
| 2310 | + out_contents.files.push_back(record); |
| 2311 | + } |
| 2312 | + } |
| 2313 | + } |
| 2314 | + } catch (const std::exception &e) { |
| 2315 | + VXCORE_LOG_ERROR("ListExternalNodes: Failed to iterate directory: %s", e.what()); |
| 2316 | + return VXCORE_ERR_IO; |
| 2317 | + } |
| 2318 | + |
| 2319 | + VXCORE_LOG_INFO("ListExternalNodes: Found %zu external files, %zu external folders", |
| 2320 | + out_contents.files.size(), out_contents.folders.size()); |
| 2321 | + return VXCORE_OK; |
| 2322 | +} |
2233 | 2323 | } // namespace vxcore |
0 commit comments