Skip to content

Commit 958fee6

Browse files
lanmowerclaude
andcommitted
refactor: extract express upload + fsbrowse setup to lib/routes-upload.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3bd1527 commit 958fee6

3 files changed

Lines changed: 82 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## [Unreleased]
22

33
### Refactor
4+
- Extract express upload + fsbrowse setup from server.js to lib/routes-upload.js (79L) exporting createExpressApp; server.js imports createExpressApp and no longer contains Busboy/fsbrowse/express inline code
45
- Extract maskKey, getProviderConfigs, saveProviderConfig, buildSystemPrompt, PROVIDER_CONFIGS from server.js to lib/provider-config.js (151L); extract logError, makeCleanupExecution, makeGetModelsForAgent, errLogPath from server.js to lib/server-utils.js (61L); server.js imports all via named imports; cleanupExecution wired after broadcastSync; _debugRoutes receives errLogPath
56
- Extract parseBody, acceptsEncoding, compressAndSend, sendJSON from server.js to lib/http-utils.js (43L); server.js imports from new module; zlib import removed from server.js
67
- Extract message/stream/queue routes (messagesMatch, streamMatch, queueMatch handlers) to lib/routes-messages.js (140L) and session/chunk/full/execution routes to lib/routes-sessions.js (145L); server.js reduced from 2406L to 2127L; both files ≤200L; wired via _messagesRoutes._match and _sessionsRoutes._match in request handler

lib/routes-upload.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import express from 'express';
2+
import Busboy from 'busboy';
3+
import fsbrowse from 'fsbrowse';
4+
import fs from 'fs';
5+
import path from 'path';
6+
7+
export function createExpressApp({ queries, BASE_URL }) {
8+
const app = express();
9+
const fsbrowseRouters = new Map();
10+
11+
app.post(BASE_URL + '/api/upload/:conversationId', (req, res) => {
12+
try {
13+
const conv = queries.getConversation(req.params.conversationId);
14+
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
15+
if (!conv.workingDirectory) return res.status(400).json({ error: 'No working directory set for this conversation' });
16+
17+
const uploadDir = conv.workingDirectory;
18+
if (!fs.existsSync(uploadDir)) {
19+
fs.mkdirSync(uploadDir, { recursive: true });
20+
}
21+
22+
const bb = Busboy({ headers: req.headers });
23+
const fileNames = [];
24+
const writePromises = [];
25+
26+
bb.on('file', (fieldname, file, info) => {
27+
const safeName = path.basename(info.filename);
28+
const filePath = path.join(uploadDir, safeName);
29+
fileNames.push(safeName);
30+
const p = new Promise((resolve) => {
31+
const writeStream = fs.createWriteStream(filePath);
32+
file.pipe(writeStream);
33+
writeStream.on('finish', resolve);
34+
writeStream.on('error', () => { file.resume(); resolve(); });
35+
});
36+
writePromises.push(p);
37+
});
38+
39+
bb.on('finish', () => {
40+
Promise.all(writePromises).then(() => {
41+
res.json({ ok: true, files: fileNames, count: fileNames.length });
42+
}).catch(() => {
43+
res.json({ ok: true, files: fileNames, count: fileNames.length });
44+
});
45+
});
46+
47+
bb.on('error', (err) => {
48+
res.status(500).json({ error: 'Upload failed: ' + err.message });
49+
});
50+
51+
req.pipe(bb);
52+
} catch (err) {
53+
res.status(500).json({ error: err.message });
54+
}
55+
});
56+
57+
app.use(BASE_URL + '/files/:conversationId', (req, res, next) => {
58+
const convId = req.params.conversationId;
59+
const conv = queries.getConversation(convId);
60+
if (!conv || !conv.workingDirectory) {
61+
return res.status(404).json({ error: 'Conversation not found or no working directory' });
62+
}
63+
64+
const normalizedWorkingDir = path.resolve(conv.workingDirectory);
65+
66+
let router = fsbrowseRouters.get(convId);
67+
if (!router) {
68+
router = fsbrowse({ baseDir: normalizedWorkingDir, name: 'Files' });
69+
fsbrowseRouters.set(convId, router);
70+
}
71+
72+
req.baseUrl = BASE_URL + '/files/' + convId;
73+
req.url = req.url.replace(new RegExp(`^${BASE_URL}/files/${convId}`), '');
74+
75+
router(req, res, next);
76+
});
77+
78+
return app;
79+
}

server.js

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import { LRUCache } from 'lru-cache';
99
import { createRequire } from 'module';
1010
import crypto from 'crypto';
1111
const PKG_VERSION = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version;
12-
import express from 'express';
13-
import Busboy from 'busboy';
14-
import fsbrowse from 'fsbrowse';
12+
import { createExpressApp } from './lib/routes-upload.js';
1513
import { queries } from './database.js';
1614
import { runClaudeWithStreaming } from './lib/claude-runner-run.js';
1715
import { initializeDescriptors, getAgentDescriptor } from './lib/agent-descriptors.js';
@@ -111,85 +109,7 @@ const STARTUP_CWD = (() => {
111109
const staticDir = path.join(rootDir, 'static');
112110
if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
113111

114-
// Express sub-app for fsbrowse file browser and file upload
115-
const expressApp = express();
116-
117-
118-
// File upload endpoint - copies dropped files to conversation workingDirectory
119-
expressApp.post(BASE_URL + '/api/upload/:conversationId', (req, res) => {
120-
try {
121-
const conv = queries.getConversation(req.params.conversationId);
122-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
123-
if (!conv.workingDirectory) return res.status(400).json({ error: 'No working directory set for this conversation' });
124-
125-
const uploadDir = conv.workingDirectory;
126-
if (!fs.existsSync(uploadDir)) {
127-
fs.mkdirSync(uploadDir, { recursive: true });
128-
}
129-
130-
const bb = Busboy({ headers: req.headers });
131-
const fileNames = [];
132-
const writePromises = [];
133-
134-
bb.on('file', (fieldname, file, info) => {
135-
const safeName = path.basename(info.filename);
136-
const filePath = path.join(uploadDir, safeName);
137-
fileNames.push(safeName);
138-
const p = new Promise((resolve) => {
139-
const writeStream = fs.createWriteStream(filePath);
140-
file.pipe(writeStream);
141-
writeStream.on('finish', resolve);
142-
writeStream.on('error', () => { file.resume(); resolve(); });
143-
});
144-
writePromises.push(p);
145-
});
146-
147-
bb.on('finish', () => {
148-
Promise.all(writePromises).then(() => {
149-
res.json({ ok: true, files: fileNames, count: fileNames.length });
150-
}).catch(() => {
151-
res.json({ ok: true, files: fileNames, count: fileNames.length });
152-
});
153-
});
154-
155-
bb.on('error', (err) => {
156-
res.status(500).json({ error: 'Upload failed: ' + err.message });
157-
});
158-
159-
req.pipe(bb);
160-
} catch (err) {
161-
res.status(500).json({ error: err.message });
162-
}
163-
});
164-
165-
// Cache fsbrowse routers per conversation to ensure API calls work
166-
const fsbrowseRouters = new Map();
167-
168-
// fsbrowse file browser - mounted per conversation workingDirectory
169-
// Route: /gm/files/:conversationId/*
170-
expressApp.use(BASE_URL + '/files/:conversationId', (req, res, next) => {
171-
const convId = req.params.conversationId;
172-
const conv = queries.getConversation(convId);
173-
if (!conv || !conv.workingDirectory) {
174-
return res.status(404).json({ error: 'Conversation not found or no working directory' });
175-
}
176-
177-
// Normalize the working directory path to avoid Windows path duplication issues
178-
const normalizedWorkingDir = path.resolve(conv.workingDirectory);
179-
180-
// Get or create cached fsbrowse router for this conversation
181-
let router = fsbrowseRouters.get(convId);
182-
if (!router) {
183-
router = fsbrowse({ baseDir: normalizedWorkingDir, name: 'Files' });
184-
fsbrowseRouters.set(convId, router);
185-
}
186-
187-
// Set baseUrl before calling the router
188-
req.baseUrl = BASE_URL + '/files/' + convId;
189-
req.url = req.url.replace(new RegExp(`^${BASE_URL}/files/${convId}`), '');
190-
191-
router(req, res, next);
192-
});
112+
const expressApp = createExpressApp({ queries, BASE_URL });
193113

194114
let discoveredAgents = [];
195115
initializeDescriptors(discoveredAgents);

0 commit comments

Comments
 (0)