Files
memind/mindspace-oa-drive/lib/paths.mjs
T
john 6d5eb15b1b feat(mindspace): add per-user OA cloud drive with fds-style file manager
Expose authenticated /api/mindspace/v1/oa/drive APIs over each user's oa/ workspace and wire a MindSpace entry at /space/oa/drive.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 18:55:46 +08:00

61 lines
1.6 KiB
JavaScript

import path from 'node:path';
export function normalizeRelPath(value) {
return String(value || '')
.trim()
.replace(/\\/g, '/')
.replace(/^\/+|\/+$/g, '');
}
export function folderNameOk(name) {
return !!name && !name.includes('/') && name !== '.' && name !== '..';
}
export function safeJoin(base, rel) {
const baseResolved = path.resolve(base);
const normalized = path.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, '');
const full = path.resolve(baseResolved, normalized);
if (full !== baseResolved && !full.startsWith(baseResolved + path.sep)) {
throw new Error('invalid path');
}
return full;
}
export function basenameOf(rel) {
const parts = normalizeRelPath(rel).split('/');
return parts[parts.length - 1] || rel;
}
export function parentOf(rel) {
const normalized = normalizeRelPath(rel);
const idx = normalized.lastIndexOf('/');
return idx >= 0 ? normalized.slice(0, idx) : '';
}
export function joinRel(parent, name) {
const p = normalizeRelPath(parent);
const n = normalizeRelPath(name);
return p ? `${p}/${n}` : n;
}
export function isReservedName(ctx, name) {
const base = basenameOf(name);
return (
[ctx.trashDirName, ctx.metaDirName, ctx.sessionDirName].includes(base)
|| base.startsWith('.drive-')
|| base.startsWith('.fds-')
);
}
export function trashRoot(ctx) {
return path.join(ctx.uploadDir, ctx.trashDirName);
}
export function metaFile(ctx) {
return path.join(ctx.uploadDir, ctx.metaDirName, 'registry.json');
}
export function sessionRoot(ctx) {
return path.join(ctx.uploadDir, ctx.sessionDirName);
}