Bump Onebox to 1.24.3 with current API/runtime dependencies, registry routing fixes, safer initial admin handling, and cleaner shutdown of Docker-backed resources.
This commit is contained in:
@@ -36,6 +36,23 @@ export class OneboxDockerManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release resources held by the Docker API client.
|
||||
*/
|
||||
async stop(): Promise<void> {
|
||||
if (!this.dockerClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.dockerClient.stop();
|
||||
} catch (error) {
|
||||
logger.error(`Failed to stop Docker client: ${getErrorMessage(error)}`);
|
||||
} finally {
|
||||
this.dockerClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure onebox network exists
|
||||
*/
|
||||
|
||||
+18
-7
@@ -66,7 +66,7 @@ export class Onebox {
|
||||
this.registry = new RegistryManager({
|
||||
dataDir: './.nogit/registry-data',
|
||||
port: 4000,
|
||||
baseUrl: 'localhost:5000',
|
||||
baseUrl: 'localhost:3000',
|
||||
});
|
||||
|
||||
// Initialize domain management
|
||||
@@ -232,23 +232,31 @@ export class Onebox {
|
||||
*/
|
||||
private async ensureDefaultUser(): Promise<void> {
|
||||
try {
|
||||
const adminUser = this.database.getUserByUsername('admin');
|
||||
const adminUsername = Deno.env.get('ONEBOX_ADMIN_USERNAME') || 'admin';
|
||||
const adminUser = this.database.getUserByUsername(adminUsername);
|
||||
|
||||
if (!adminUser) {
|
||||
logger.info('Creating default admin user...');
|
||||
logger.info(`Creating initial admin user ${adminUsername}...`);
|
||||
|
||||
const passwordHash = await hashPassword('admin');
|
||||
const configuredPassword = Deno.env.get('ONEBOX_ADMIN_PASSWORD');
|
||||
const initialPassword = configuredPassword || crypto.randomUUID().replaceAll('-', '');
|
||||
const passwordHash = await hashPassword(initialPassword);
|
||||
|
||||
await this.database.createUser({
|
||||
username: 'admin',
|
||||
username: adminUsername,
|
||||
passwordHash,
|
||||
role: 'admin',
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
|
||||
logger.warn('Default admin user created with username: admin, password: admin');
|
||||
logger.warn('IMPORTANT: Change the default password immediately!');
|
||||
if (configuredPassword) {
|
||||
logger.warn(`Initial admin user created from ONEBOX_ADMIN_PASSWORD: ${adminUsername}`);
|
||||
} else {
|
||||
logger.warn(`Initial admin user created: ${adminUsername}`);
|
||||
logger.warn(`Generated one-time admin password: ${initialPassword}`);
|
||||
}
|
||||
logger.warn('Change the initial admin password immediately.');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create default user: ${getErrorMessage(error)}`);
|
||||
@@ -454,6 +462,9 @@ export class Onebox {
|
||||
// Close backup archive
|
||||
await this.backupManager.close();
|
||||
|
||||
// Release Docker client resources after all Docker-backed managers stopped.
|
||||
await this.docker.stop();
|
||||
|
||||
// Close database
|
||||
this.database.close();
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@ export class ProxyLogReceiver {
|
||||
private port: number;
|
||||
private running = false;
|
||||
private connections: Set<Deno.TcpConn> = new Set();
|
||||
private connectionReaders: Map<Deno.TcpConn, ReadableStreamDefaultReader<Uint8Array>> = new Map();
|
||||
private connectionHandlers: Set<Promise<void>> = new Set();
|
||||
private acceptTask: Promise<void> | null = null;
|
||||
|
||||
// Adaptive sampling state
|
||||
private logCountWindow: number[] = []; // timestamps of recent logs
|
||||
@@ -174,7 +177,7 @@ export class ProxyLogReceiver {
|
||||
logger.success(`ProxyLogReceiver started on TCP port ${this.port}`);
|
||||
|
||||
// Start accepting connections in background
|
||||
this.acceptConnections();
|
||||
this.acceptTask = this.acceptConnections();
|
||||
} catch (error) {
|
||||
logger.error(`Failed to start ProxyLogReceiver: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
@@ -190,7 +193,9 @@ export class ProxyLogReceiver {
|
||||
try {
|
||||
for await (const conn of this.server) {
|
||||
this.connections.add(conn);
|
||||
this.handleConnection(conn);
|
||||
const handlerTask = this.handleConnection(conn);
|
||||
this.connectionHandlers.add(handlerTask);
|
||||
handlerTask.finally(() => this.connectionHandlers.delete(handlerTask));
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.running) {
|
||||
@@ -207,6 +212,7 @@ export class ProxyLogReceiver {
|
||||
logger.debug(`ProxyLogReceiver: Connection from ${remoteAddr.hostname}:${remoteAddr.port}`);
|
||||
|
||||
const reader = conn.readable.getReader();
|
||||
this.connectionReaders.set(conn, reader);
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
@@ -232,7 +238,13 @@ export class ProxyLogReceiver {
|
||||
logger.debug(`ProxyLogReceiver connection closed: ${getErrorMessage(error)}`);
|
||||
}
|
||||
} finally {
|
||||
this.connectionReaders.delete(conn);
|
||||
this.connections.delete(conn);
|
||||
try {
|
||||
reader.releaseLock();
|
||||
} catch {
|
||||
// Reader may already be released after cancellation during shutdown.
|
||||
}
|
||||
try {
|
||||
conn.close();
|
||||
} catch {
|
||||
@@ -447,6 +459,11 @@ export class ProxyLogReceiver {
|
||||
|
||||
this.running = false;
|
||||
|
||||
// Cancel pending reads before closing sockets so background handlers can finish.
|
||||
await Promise.allSettled(
|
||||
Array.from(this.connectionReaders.values()).map((reader) => reader.cancel()),
|
||||
);
|
||||
|
||||
// Close all connections
|
||||
for (const conn of this.connections) {
|
||||
try {
|
||||
@@ -467,6 +484,15 @@ export class ProxyLogReceiver {
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
if (this.acceptTask) {
|
||||
await this.acceptTask.catch(() => {});
|
||||
this.acceptTask = null;
|
||||
}
|
||||
|
||||
await Promise.allSettled(this.connectionHandlers);
|
||||
this.connectionHandlers.clear();
|
||||
this.connectionReaders.clear();
|
||||
|
||||
// Clear clients
|
||||
this.clients.clear();
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export class RegistryManager {
|
||||
},
|
||||
ociTokens: {
|
||||
enabled: true,
|
||||
realm: 'http://localhost:3000/v2/token',
|
||||
realm: `http://${this.baseUrl}/v2/token`,
|
||||
service: 'onebox-registry',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -233,13 +233,13 @@ export class SmartProxyManager {
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (!this.serviceRunning && !(await this.getExistingService())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.ensureDockerClient();
|
||||
|
||||
if (!this.serviceRunning && !(await this.getExistingService())) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Stopping SmartProxy service...');
|
||||
await this.removeService();
|
||||
|
||||
@@ -247,6 +247,16 @@ export class SmartProxyManager {
|
||||
logger.info('SmartProxy service stopped');
|
||||
} catch (error) {
|
||||
logger.error(`Failed to stop SmartProxy: ${getErrorMessage(error)}`);
|
||||
} finally {
|
||||
if (this.dockerClient) {
|
||||
try {
|
||||
await this.dockerClient.stop();
|
||||
} catch (error) {
|
||||
logger.error(`Failed to stop SmartProxy Docker client: ${getErrorMessage(error)}`);
|
||||
} finally {
|
||||
this.dockerClient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user