feat: Add log streaming functionality for Docker containers and improve platform service type handling in HTTP server
This commit is contained in:
@@ -939,4 +939,49 @@ export class OneboxDockerManager {
|
||||
}
|
||||
return this.dockerClient.listContainers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream container logs continuously
|
||||
* @param containerID The container ID
|
||||
* @param callback Callback for each log line (line, isError)
|
||||
*/
|
||||
async streamContainerLogs(
|
||||
containerID: string,
|
||||
callback: (line: string, isError: boolean) => void
|
||||
): Promise<void> {
|
||||
try {
|
||||
const container = await this.dockerClient!.getContainerById(containerID);
|
||||
|
||||
if (!container) {
|
||||
throw new Error(`Container not found: ${containerID}`);
|
||||
}
|
||||
|
||||
const logStream = await container.streamLogs({
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
timestamps: true,
|
||||
tail: 100,
|
||||
});
|
||||
|
||||
logStream.on('data', (chunk: Uint8Array) => {
|
||||
// Docker multiplexes stdout/stderr with 8-byte header
|
||||
// Byte 0: stream type (1=stdout, 2=stderr)
|
||||
// Bytes 4-7: frame size (big-endian)
|
||||
// Rest: actual log data
|
||||
const streamType = chunk[0];
|
||||
const isError = streamType === 2;
|
||||
const content = new TextDecoder().decode(chunk.slice(8));
|
||||
if (content.trim()) {
|
||||
callback(content.trim(), isError);
|
||||
}
|
||||
});
|
||||
|
||||
logStream.on('error', (err: Error) => {
|
||||
logger.error(`Log stream error for ${containerID}: ${err.message}`);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`Failed to stream logs for ${containerID}: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,13 +275,13 @@ export class OneboxHttpServer {
|
||||
} else if (path === '/api/platform-services' && method === 'GET') {
|
||||
return await this.handleListPlatformServicesRequest();
|
||||
} else if (path.match(/^\/api\/platform-services\/(mongodb|minio|redis|postgresql|rabbitmq)$/) && method === 'GET') {
|
||||
const type = path.split('/').pop()!;
|
||||
const type = path.split('/').pop()! as TPlatformServiceType;
|
||||
return await this.handleGetPlatformServiceRequest(type);
|
||||
} else if (path.match(/^\/api\/platform-services\/(mongodb|minio|redis|postgresql|rabbitmq)\/start$/) && method === 'POST') {
|
||||
const type = path.split('/')[3];
|
||||
const type = path.split('/')[3] as TPlatformServiceType;
|
||||
return await this.handleStartPlatformServiceRequest(type);
|
||||
} else if (path.match(/^\/api\/platform-services\/(mongodb|minio|redis|postgresql|rabbitmq)\/stop$/) && method === 'POST') {
|
||||
const type = path.split('/')[3];
|
||||
const type = path.split('/')[3] as TPlatformServiceType;
|
||||
return await this.handleStopPlatformServiceRequest(type);
|
||||
} else if (path.match(/^\/api\/services\/[^/]+\/platform-resources$/) && method === 'GET') {
|
||||
const serviceName = path.split('/')[3];
|
||||
|
||||
@@ -45,10 +45,10 @@ export class RegistryManager {
|
||||
this.s3Server = await plugins.smarts3.Smarts3.createAndStart({
|
||||
server: {
|
||||
port: port,
|
||||
host: '0.0.0.0',
|
||||
address: '0.0.0.0',
|
||||
},
|
||||
storage: {
|
||||
bucketsDir: dataDir,
|
||||
directory: dataDir,
|
||||
cleanSlate: false, // Preserve data across restarts
|
||||
},
|
||||
});
|
||||
@@ -70,9 +70,13 @@ export class RegistryManager {
|
||||
},
|
||||
auth: {
|
||||
jwtSecret: this.jwtSecret,
|
||||
tokenStore: 'memory',
|
||||
npmTokens: {
|
||||
enabled: false,
|
||||
},
|
||||
ociTokens: {
|
||||
enabled: true,
|
||||
issuer: 'onebox-registry',
|
||||
realm: 'onebox-registry',
|
||||
service: 'onebox-registry',
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user