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>
29 lines
1.0 KiB
JavaScript
29 lines
1.0 KiB
JavaScript
import fs from 'node:fs';
|
|
import fsp from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { safeJoin } from './paths.mjs';
|
|
|
|
export async function createZipStream(ctx, items) {
|
|
const existing = [];
|
|
for (const rel of items) {
|
|
const full = safeJoin(ctx.uploadDir, rel);
|
|
if (fs.existsSync(full)) existing.push({ rel, full });
|
|
}
|
|
if (!existing.length) throw new Error('no files');
|
|
|
|
const tmpZip = path.join(ctx.uploadDir, ctx.metaDirName, `download-${Date.now()}.zip`);
|
|
await fsp.mkdir(path.dirname(tmpZip), { recursive: true });
|
|
|
|
await new Promise((resolve, reject) => {
|
|
const args = ['-r', tmpZip, ...existing.map((x) => x.full)];
|
|
const proc = spawn('zip', args, { cwd: ctx.uploadDir });
|
|
proc.on('error', reject);
|
|
proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`zip exit ${code}`))));
|
|
});
|
|
|
|
const stream = fs.createReadStream(tmpZip);
|
|
stream.on('close', () => fsp.unlink(tmpZip).catch(() => {}));
|
|
return stream;
|
|
}
|