u
This commit is contained in:
@@ -53,7 +53,6 @@ export function register(app, basePath = '') {
|
||||
* | `modelconversation` | `{}` | `POST /modelconversation`, `DELETE /modelconversation/{id}` |
|
||||
* | `modelconversation-<id>` | `{}` | Conversation-specific updates on the same model conversation thread; the concrete ID is used in the event name |
|
||||
* | `brand` | `{ sender, brandId }` | `POST /brand`, `PATCH /brand/{id}`, `DELETE /brand/{id}`, `POST /brand/{id}/image`, `DELETE /brand/{id}/image` |
|
||||
* | `draft` | `{ sender, action, name, content }` | Draft update on `drafts/quote`, `drafts/order`, `drafts/delivery` (`sender` echoes the caller's `x-client-id`) |
|
||||
*
|
||||
* The `brand` payload is `{ sender, brandId }`: `sender` echoes the
|
||||
* caller's `x-client-id` header (empty string if absent) so the sender
|
||||
@@ -96,8 +95,6 @@ export function register(app, basePath = '') {
|
||||
* payload: "{}"
|
||||
* - name: brand
|
||||
* payload: "{ sender, brandId }"
|
||||
* - name: draft
|
||||
* payload: "{ sender, action, name, content }"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: |
|
||||
|
||||
@@ -11,12 +11,6 @@ import {
|
||||
restoreDefaultApplets,
|
||||
} from '../../tools/vfs.js';
|
||||
import sharp from 'sharp';
|
||||
import {
|
||||
parseDraftPath,
|
||||
requestDraftRead,
|
||||
requestDraftList,
|
||||
notifyDraftUpdate,
|
||||
} from '../../services/draftManager.js';
|
||||
|
||||
// #region Helpers
|
||||
|
||||
@@ -478,18 +472,7 @@ export function register(app, basePath = '') {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
const diskFiles = walk(vfsRoot);
|
||||
let drafts = [];
|
||||
try {
|
||||
drafts = await requestDraftList({
|
||||
companyId: selected.companyId,
|
||||
userId: req.user.id,
|
||||
clientId: req.headers?.['x-client-id'],
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to request drafts from frontend:', err);
|
||||
}
|
||||
const files = [...diskFiles, ...drafts].sort((a, b) => a.name.localeCompare(b.name));
|
||||
const files = walk(vfsRoot).sort((a, b) => a.name.localeCompare(b.name));
|
||||
return res.json({ files });
|
||||
} catch (error) {
|
||||
const status = vfsFileErrorStatus(error);
|
||||
@@ -705,9 +688,6 @@ export function register(app, basePath = '') {
|
||||
if (isReadOnlyDatabasePath(parsed.name)) {
|
||||
return res.status(403).json({ error: 'Cannot create directories in read-only database paths' });
|
||||
}
|
||||
if (parseDraftPath(parsed.name).isFolder || parseDraftPath(parsed.name).isDraft) {
|
||||
return res.status(403).json({ error: 'Cannot create directories in virtual drafts folder' });
|
||||
}
|
||||
|
||||
try {
|
||||
const vfsRoot = await resolveVfsRoot(req.user.id);
|
||||
@@ -816,14 +796,6 @@ export function register(app, basePath = '') {
|
||||
if (isReadOnlyDatabasePath(parsedOld.name) || isReadOnlyDatabasePath(parsedNew.name)) {
|
||||
return res.status(403).json({ error: 'Cannot rename read-only database paths' });
|
||||
}
|
||||
if (
|
||||
parseDraftPath(parsedOld.name).isFolder ||
|
||||
parseDraftPath(parsedOld.name).isDraft ||
|
||||
parseDraftPath(parsedNew.name).isFolder ||
|
||||
parseDraftPath(parsedNew.name).isDraft
|
||||
) {
|
||||
return res.status(403).json({ error: 'Cannot rename virtual draft files or folders' });
|
||||
}
|
||||
|
||||
try {
|
||||
const vfsRoot = await resolveVfsRoot(req.user.id);
|
||||
@@ -967,50 +939,6 @@ export function register(app, basePath = '') {
|
||||
return res.status(403).json({ error: 'Cannot modify read-only database files' });
|
||||
}
|
||||
|
||||
const draftInfo = parseDraftPath(parsed.name);
|
||||
if (draftInfo.isFolder) {
|
||||
return res.status(400).json({ error: 'Cannot write directly to directory' });
|
||||
}
|
||||
if (draftInfo.isDraft) {
|
||||
let strContent;
|
||||
if (typeof req.body?.content === 'string') {
|
||||
strContent = req.body.content;
|
||||
} else if (req.body && typeof req.body === 'object' && !Buffer.isBuffer(req.body)) {
|
||||
strContent = req.body.content !== undefined
|
||||
? (typeof req.body.content === 'string' ? req.body.content : JSON.stringify(req.body.content))
|
||||
: JSON.stringify(req.body);
|
||||
} else if (Buffer.isBuffer(req.body)) {
|
||||
strContent = req.body.toString('utf8');
|
||||
} else if (typeof req.body === 'string') {
|
||||
strContent = req.body;
|
||||
} else {
|
||||
return res.status(400).json({ error: 'No content provided' });
|
||||
}
|
||||
|
||||
let parsedContent = strContent;
|
||||
try {
|
||||
parsedContent = JSON.parse(strContent);
|
||||
} catch {}
|
||||
|
||||
const updated = notifyDraftUpdate({
|
||||
companyId: selected.companyId,
|
||||
userId: req.user.id,
|
||||
clientId: req.headers?.['x-client-id'],
|
||||
name: draftInfo.name,
|
||||
content: parsedContent,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
file: {
|
||||
name: `drafts/${draftInfo.name}`,
|
||||
size: updated.size,
|
||||
mime: 'application/json; charset=utf-8',
|
||||
updated_at: updated.updatedAt,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let buffer;
|
||||
let mime = parsed.mime;
|
||||
if (typeof req.body?.content === 'string') {
|
||||
@@ -1123,10 +1051,6 @@ export function register(app, basePath = '') {
|
||||
if (isReadOnlyDatabasePath(parsed.name)) {
|
||||
return res.status(403).json({ error: 'Cannot delete read-only database files' });
|
||||
}
|
||||
const draftDeleteInfo = parseDraftPath(parsed.name);
|
||||
if (draftDeleteInfo.isFolder || draftDeleteInfo.isDraft) {
|
||||
return res.status(403).json({ error: 'Cannot delete virtual draft files' });
|
||||
}
|
||||
|
||||
try {
|
||||
const vfsRoot = await resolveVfsRoot(req.user.id);
|
||||
@@ -1294,33 +1218,7 @@ export function register(app, basePath = '') {
|
||||
return res.status(400).json({ error: parsed.error });
|
||||
}
|
||||
|
||||
const draftGetInfo = parseDraftPath(parsed.name);
|
||||
if (draftGetInfo.isFolder) {
|
||||
return res.status(400).json({ error: 'Cannot download a directory directly' });
|
||||
}
|
||||
if (draftGetInfo.isDraft) {
|
||||
try {
|
||||
const content = await requestDraftRead({
|
||||
companyId: selected.companyId,
|
||||
userId: req.user.id,
|
||||
clientId: req.headers?.['x-client-id'],
|
||||
name: draftGetInfo.name,
|
||||
});
|
||||
const strContent = typeof content === 'string' ? content : JSON.stringify(content ?? {}, null, 2);
|
||||
const buffer = Buffer.from(strContent, 'utf8');
|
||||
const effectiveMime = 'application/json; charset=utf-8';
|
||||
const fallbackName = `${draftGetInfo.name}.json`;
|
||||
|
||||
res.setHeader('Content-Type', effectiveMime);
|
||||
res.setHeader('Content-Length', String(buffer.length));
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
|
||||
res.setHeader('Content-Disposition', `inline; filename="${fallbackName}"`);
|
||||
return res.status(200).send(buffer);
|
||||
} catch (err) {
|
||||
return res.status(404).json({ error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const vfsRoot = await resolveVfsRoot(req.user.id);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { parseVfsRef, vfsFileErrorStatus } from './vfs.js';
|
||||
// VFS paths that are owned by their own literal route in `vfs.js`. They are registered as
|
||||
// separate Express handlers, so this wildcard must defer to them instead of treating e.g.
|
||||
// `/vfs/upload` as an applet path. Deferring keeps the two files order-independent.
|
||||
const RESERVED_VFS_SEGMENTS = new Set(['upload', 'mkdir', 'rename', 'drafts', 'skills', 'applets']);
|
||||
const RESERVED_VFS_SEGMENTS = new Set(['upload', 'mkdir', 'rename', 'skills', 'applets']);
|
||||
|
||||
function requireSelectedCompany(req) {
|
||||
const companyId = req.user?.selectedCompanyId;
|
||||
|
||||
Reference in New Issue
Block a user