6d5eb15b1b
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>
61 lines
1.6 KiB
JavaScript
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);
|
|
}
|