Files
memind/workspace-storage.mjs
john 637b32515a fix(mindspace): sync nested OA files and count workspace assets
Recursive workspace sync now imports files in oa/private/public subfolders,
references workspace paths without duplicating storage, and includes
workspace-sourced assets in category itemCount. Refresh counts when leaving
a category view.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 10:34:18 +08:00

42 lines
1.8 KiB
JavaScript

import path from 'node:path';
import { resolveUserWorkspaceRoot, resolveZoneFilePath } from './user-space.mjs';
export const WORKSPACE_STORAGE_PREFIX = 'workspace://';
export function buildWorkspaceStorageKey(userId, categoryCode, relativeFilename) {
const category = String(categoryCode ?? '').trim();
const relativePath = String(relativeFilename ?? '').replace(/\\/g, '/').replace(/^\/+/, '');
if (!userId || !category || !relativePath) {
throw new Error('invalid workspace storage key parts');
}
if (relativePath.split('/').some((part) => part === '..' || part === '.')) {
throw new Error('invalid workspace relative path');
}
return `${WORKSPACE_STORAGE_PREFIX}${userId}/${category}/${relativePath}`;
}
export function isWorkspaceStorageKey(storageKey) {
return String(storageKey ?? '').startsWith(WORKSPACE_STORAGE_PREFIX);
}
export function resolveWorkspaceStoragePath(h5Root, storageKey) {
if (!h5Root || !isWorkspaceStorageKey(storageKey)) return null;
const rest = storageKey.slice(WORKSPACE_STORAGE_PREFIX.length);
const slash = rest.indexOf('/');
if (slash <= 0) return null;
const userId = rest.slice(0, slash);
const remainder = rest.slice(slash + 1);
const slash2 = remainder.indexOf('/');
if (slash2 <= 0) return null;
const categoryCode = remainder.slice(0, slash2);
const relativeFilename = remainder.slice(slash2 + 1);
if (!relativeFilename) return null;
const workspaceRoot = resolveUserWorkspaceRoot(h5Root, { id: userId });
const resolved = path.resolve(resolveZoneFilePath(workspaceRoot, categoryCode, relativeFilename));
const zoneRoot = path.resolve(resolveZoneFilePath(workspaceRoot, categoryCode, ''));
if (resolved !== zoneRoot && !resolved.startsWith(`${zoneRoot}${path.sep}`)) {
return null;
}
return resolved;
}