Skip to content

Commit 6192c2f

Browse files
committed
record the reason an install failed
Install state now stores an error message alongside the failed status and the container status route returns it, so panels can tell users why the install broke instead of a bare 'failed'.
1 parent ff12794 commit 6192c2f

2 files changed

Lines changed: 32 additions & 13 deletions

File tree

src/handlers/installState.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,52 @@ import { join } from 'node:path';
22

33
const logsPath = join(process.cwd(), 'storage/install_logs.json');
44

5-
async function readState(): Promise<Record<string, string>> {
5+
export interface InstallStatus {
6+
state: string;
7+
error?: string;
8+
}
9+
10+
async function readState(): Promise<Record<string, InstallStatus>> {
611
try {
712
const file = Bun.file(logsPath);
813
const text = await file.text();
9-
return JSON.parse(text);
14+
const parsed = JSON.parse(text);
15+
const entries = Object.entries(parsed).map(([id, value]) => {
16+
if (value && typeof value === 'object') {
17+
return [id, value] as [string, InstallStatus];
18+
}
19+
// legacy flat string format
20+
return [id, { state: String(value) }] as [string, InstallStatus];
21+
});
22+
return Object.fromEntries(entries);
1023
} catch {
1124
return {};
1225
}
1326
}
1427

15-
async function writeState(data: Record<string, string>): Promise<void> {
28+
async function writeState(data: Record<string, InstallStatus>): Promise<void> {
1629
await Bun.write(logsPath, JSON.stringify(data, null, 2));
1730
}
1831

19-
export async function setServerState(containerId: string, state: string): Promise<void> {
32+
export async function setServerState(containerId: string, state: string, error?: string): Promise<void> {
2033
const logs = await readState();
21-
logs[containerId] = state;
34+
logs[containerId] = error ? { state, error } : { state };
2235
await writeState(logs);
2336
}
2437

2538
export async function getServerState(containerId: string): Promise<string | undefined> {
39+
const logs = await readState();
40+
return logs[containerId]?.state;
41+
}
42+
43+
export async function getInstallStatus(containerId: string): Promise<InstallStatus | undefined> {
2644
const logs = await readState();
2745
return logs[containerId];
2846
}
2947

3048
export async function getAllServerStates(): Promise<Record<string, string>> {
31-
return readState();
49+
const logs = await readState();
50+
return Object.fromEntries(Object.entries(logs).map(([id, s]) => [id, s.state]));
3251
}
3352

3453
export async function removeServerState(containerId: string): Promise<void> {

src/routes/instances.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
stopContainer,
1818
} from '../handlers/docker';
1919
import { copyIntoVolume, downloadToVolume } from '../handlers/fs';
20-
import { getServerState, setServerState } from '../handlers/installState';
20+
import { getInstallStatus, setServerState } from '../handlers/installState';
2121
import logger from '../logger';
2222
import { validateContainerId } from '../validation';
2323
import { clearLogBuffer, getLogBuffer } from '../ws/server';
@@ -176,7 +176,7 @@ export async function handleContainerInstaller(req: Request): Promise<Response>
176176
return json({ message: `container ${id} installed successfully` });
177177
} catch (error) {
178178
logger.error('error installing container', error);
179-
await setServerState(id, 'failed');
179+
await setServerState(id, 'failed', error instanceof Error ? error.message : String(error));
180180
return json({ error: `failed to install container ${id}` }, 500);
181181
}
182182
}
@@ -209,7 +209,7 @@ export async function handleContainerInstall(req: Request): Promise<Response> {
209209
await setServerState(id, 'installed');
210210
} catch (err) {
211211
logger.error('error during async install', err);
212-
await setServerState(id, 'failed');
212+
await setServerState(id, 'failed', err instanceof Error ? err.message : String(err));
213213
}
214214
})();
215215

@@ -246,7 +246,7 @@ export async function handleContainerReinstall(req: Request): Promise<Response>
246246
await setServerState(id, 'installed');
247247
} catch (err) {
248248
logger.error('error during async reinstall', err);
249-
await setServerState(id, 'failed');
249+
await setServerState(id, 'failed', err instanceof Error ? err.message : String(err));
250250
}
251251
})();
252252

@@ -347,9 +347,9 @@ export async function handleContainerInstallStatus(_req: Request, params: Record
347347
if (!id) return json({ error: 'container ID is required' }, 400);
348348
if (!validateContainerId(id)) return json({ error: 'invalid container ID' }, 400);
349349

350-
const state = await getServerState(id);
351-
if (!state) return json({ message: `no install state found for container ${id}` }, 404);
352-
return json({ containerId: id, state });
350+
const status = await getInstallStatus(id);
351+
if (!status) return json({ message: `no install state found for container ${id}` }, 404);
352+
return json({ containerId: id, state: status.state, error: status.error });
353353
}
354354

355355
export async function handleContainerStart(req: Request): Promise<Response> {

0 commit comments

Comments
 (0)