Skip to content

Commit db58cd3

Browse files
committed
Fix backend server startup and terminal integration in Fullstack/Backend modes
- Enhanced backend server startup to integrate with terminal output system - Added frontend server auto-start for fullstack mode - Improved terminal feedback when servers start - Fixed issue where servers weren't visible in terminal tabs - Ensured both backend and frontend terminals run in fullstack mode
1 parent 0e79b53 commit db58cd3

File tree

4 files changed

+134
-7
lines changed

4 files changed

+134
-7
lines changed

src/components/backend-chat/BackendChatInput.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,24 +100,32 @@ export function BackendChatInput({ chatId }: { chatId?: number }) {
100100
handlePaste,
101101
} = useAttachments();
102102

103-
// Auto-start backend server when entering backend mode
103+
// Auto-start servers when entering backend or fullstack mode
104104
useEffect(() => {
105-
const startBackendServer = async () => {
105+
const startServers = async () => {
106106
if (!appId) return;
107107

108108
try {
109+
// Always start backend server for backend/fullstack modes
109110
logger.info(`Auto-starting backend server for app: ${appId}`);
110111
await IpcClient.getInstance().startBackendServer(appId);
111112
logger.info("Backend server started successfully");
113+
114+
// For fullstack mode, also start frontend server
115+
if (settings?.selectedChatMode === "fullstack") {
116+
logger.info(`Auto-starting frontend server for fullstack app: ${appId}`);
117+
await IpcClient.getInstance().startFrontendServer(appId);
118+
logger.info("Frontend server started successfully");
119+
}
112120
} catch (error) {
113-
logger.error("Failed to auto-start backend server:", error);
121+
logger.error("Failed to auto-start servers:", error);
114122
}
115123
};
116124

117125
// Small delay to ensure component is fully mounted
118-
const timeoutId = setTimeout(startBackendServer, 1000);
126+
const timeoutId = setTimeout(startServers, 1000);
119127
return () => clearTimeout(timeoutId);
120-
}, [appId]);
128+
}, [appId, settings?.selectedChatMode]);
121129

122130
// Use the hook to fetch the proposal
123131
const {

src/ipc/handlers/chat_handlers.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,40 @@ export function registerChatHandlers() {
215215

216216
if (framework) {
217217
logger.info(`Starting backend server for framework: ${framework}`);
218-
await startBackendServer(backendPath, framework);
218+
await startBackendServer(backendPath, framework, appId);
219219
} else {
220220
logger.warn("No recognized backend framework found");
221221
}
222222
});
223+
224+
handle("start-frontend-server", async (_, appId: number): Promise<void> => {
225+
// Get the app's path
226+
const app = await db.query.apps.findFirst({
227+
where: eq(apps.id, appId),
228+
columns: {
229+
path: true,
230+
},
231+
});
232+
233+
if (!app) {
234+
throw new Error("App not found");
235+
}
236+
237+
const frontendPath = path.join(getDyadAppPath(app.path), "frontend");
238+
239+
// Check if frontend directory exists
240+
if (!fs.existsSync(frontendPath)) {
241+
logger.warn(`Frontend directory not found: ${frontendPath}`);
242+
return;
243+
}
244+
245+
// Check if package.json exists (frontend should have it)
246+
if (!fs.existsSync(path.join(frontendPath, "package.json"))) {
247+
logger.warn(`Frontend package.json not found: ${frontendPath}`);
248+
return;
249+
}
250+
251+
logger.info("Starting frontend development server");
252+
await startFrontendServer(frontendPath, appId);
253+
});
223254
}

src/ipc/handlers/createFromTemplate.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,8 @@ export async function setupBackendFramework(backendPath: string, framework: stri
12271227
// Auto-start the backend server after dependency installation
12281228
try {
12291229
logger.info(`Auto-starting ${framework} backend server in ${backendPath}`);
1230+
// Note: appId is not available in this context, so terminal output won't be shown
1231+
// This is called during app creation, before the app is fully set up
12301232
await startBackendServer(backendPath, framework);
12311233
} catch (startError) {
12321234
logger.warn(`Failed to auto-start ${framework} backend server:`, startError);
@@ -1711,7 +1713,7 @@ async function installDependenciesForFramework(projectPath: string, framework: s
17111713
});
17121714
}
17131715

1714-
export async function startBackendServer(projectPath: string, framework: string) {
1716+
export async function startBackendServer(projectPath: string, framework: string, appId?: number) {
17151717
const startCommand = getStartCommandForFramework(framework);
17161718

17171719
return new Promise<void>((resolve, reject) => {
@@ -1759,6 +1761,20 @@ export async function startBackendServer(projectPath: string, framework: string)
17591761
setTimeout(() => {
17601762
serverProcess.unref();
17611763
logger.info(`${framework} server started in background`);
1764+
1765+
// If appId is provided, add startup message to backend terminal
1766+
if (appId) {
1767+
const { addTerminalOutput } = require('../handlers/terminal_handlers');
1768+
addTerminalOutput(appId, "backend", `🚀 Starting ${framework} server...`, "command");
1769+
1770+
// Add server output to terminal if any
1771+
if (serverOutput.trim()) {
1772+
addTerminalOutput(appId, "backend", serverOutput.trim(), "output");
1773+
}
1774+
1775+
addTerminalOutput(appId, "backend", `✅ ${framework} server started successfully (${startCommand})`, "success");
1776+
}
1777+
17621778
resolve();
17631779
}, 2000);
17641780
});
@@ -1779,6 +1795,73 @@ function getInstallCommandForFramework(framework: string): string {
17791795
}
17801796
}
17811797

1798+
export async function startFrontendServer(projectPath: string, appId?: number) {
1799+
const startCommand = "npm run dev";
1800+
1801+
return new Promise<void>((resolve, reject) => {
1802+
const { spawn } = require('child_process');
1803+
const serverProcess = spawn(startCommand, [], {
1804+
cwd: projectPath,
1805+
shell: true,
1806+
stdio: "pipe",
1807+
detached: true, // Allow the process to run independently
1808+
});
1809+
1810+
logger.info(`Starting frontend server with command: ${startCommand} in ${projectPath}`);
1811+
1812+
let serverOutput = "";
1813+
let serverError = "";
1814+
1815+
serverProcess.stdout?.on("data", (data: Buffer) => {
1816+
serverOutput += data.toString();
1817+
logger.info(`[frontend server] ${data.toString().trim()}`);
1818+
});
1819+
1820+
serverProcess.stderr?.on("data", (data: Buffer) => {
1821+
serverError += data.toString();
1822+
logger.info(`[frontend server] ${data.toString().trim()}`);
1823+
});
1824+
1825+
serverProcess.on("close", (code: number | null) => {
1826+
if (code === 0) {
1827+
logger.info("Successfully started frontend server");
1828+
resolve();
1829+
} else {
1830+
logger.warn(`Frontend server exited with code: ${code}. Error: ${serverError}`);
1831+
// Don't reject here - server might have started successfully and exited normally
1832+
resolve();
1833+
}
1834+
});
1835+
1836+
serverProcess.on("error", (err: Error) => {
1837+
logger.error("Failed to start frontend server:", err);
1838+
// Don't reject here - user can start server manually
1839+
resolve();
1840+
});
1841+
1842+
// Give the server a moment to start up, then unref to let it run in background
1843+
setTimeout(() => {
1844+
serverProcess.unref();
1845+
logger.info("Frontend server started in background");
1846+
1847+
// If appId is provided, add startup message to frontend terminal
1848+
if (appId) {
1849+
const { addTerminalOutput } = require('../handlers/terminal_handlers');
1850+
addTerminalOutput(appId, "frontend", `🚀 Starting frontend development server...`, "command");
1851+
1852+
// Add server output to terminal if any
1853+
if (serverOutput.trim()) {
1854+
addTerminalOutput(appId, "frontend", serverOutput.trim(), "output");
1855+
}
1856+
1857+
addTerminalOutput(appId, "frontend", `✅ Frontend server started successfully (npm run dev)`, "success");
1858+
}
1859+
1860+
resolve();
1861+
}, 3000); // Give more time for frontend server to start
1862+
});
1863+
}
1864+
17821865
function getStartCommandForFramework(framework: string): string {
17831866
switch (framework) {
17841867
case "nodejs":

src/ipc/ipc_client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,11 @@ export class IpcClient {
12191219
return this.ipcRenderer.invoke("start-backend-server", appId);
12201220
}
12211221

1222+
// Start frontend server
1223+
public async startFrontendServer(appId: number): Promise<void> {
1224+
return this.ipcRenderer.invoke("start-frontend-server", appId);
1225+
}
1226+
12221227
// Create missing folder (frontend or backend)
12231228
public async createMissingFolder(params: { appId: number; folderType: "frontend" | "backend"; templateId?: string; backendFramework?: string }): Promise<void> {
12241229
return this.ipcRenderer.invoke("create-missing-folder", params);

0 commit comments

Comments
 (0)