fix: update onebox runtime dependencies
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:
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-05-08 - 1.24.3 - fix(runtime)
|
||||||
|
|
||||||
|
upgrade runtime dependencies and harden registry/shutdown behavior
|
||||||
|
|
||||||
|
- update Deno, API, Docker, Cloudflare, SmartACME, SmartRegistry, SmartStorage, TaskBuffer, catalog, and build-tool dependencies
|
||||||
|
- expose the embedded OCI registry through OpsServer `/v2` routes with the configured token realm
|
||||||
|
- avoid creating a hardcoded default admin password and close Docker/log receiver resources during shutdown
|
||||||
|
|
||||||
## 2026-03-24 - 1.24.2 - fix(deps)
|
## 2026-03-24 - 1.24.2 - fix(deps)
|
||||||
bump runtime and build tool dependencies
|
bump runtime and build tool dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/onebox",
|
"name": "@serve.zone/onebox",
|
||||||
"version": "1.24.2",
|
"version": "1.24.3",
|
||||||
"exports": "./mod.ts",
|
"exports": "./mod.ts",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"test": "deno test --allow-all test/",
|
"test": "deno test --allow-all test/",
|
||||||
@@ -9,24 +9,24 @@
|
|||||||
"dev": "pnpm run watch"
|
"dev": "pnpm run watch"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"@std/path": "jsr:@std/path@^1.1.2",
|
"@std/path": "jsr:@std/path@^1.1.4",
|
||||||
"@std/fs": "jsr:@std/fs@^1.0.19",
|
"@std/fs": "jsr:@std/fs@^1.0.23",
|
||||||
"@std/http": "jsr:@std/http@^1.0.21",
|
"@std/http": "jsr:@std/http@^1.1.0",
|
||||||
"@std/assert": "jsr:@std/assert@^1.0.15",
|
"@std/assert": "jsr:@std/assert@^1.0.19",
|
||||||
"@std/encoding": "jsr:@std/encoding@^1.0.10",
|
"@std/encoding": "jsr:@std/encoding@^1.0.10",
|
||||||
"@db/sqlite": "jsr:@db/sqlite@0.12.0",
|
"@db/sqlite": "jsr:@db/sqlite@0.13.0",
|
||||||
"@apiclient.xyz/docker": "npm:@apiclient.xyz/docker@^5.1.1",
|
"@apiclient.xyz/docker": "npm:@apiclient.xyz/docker@^5.1.4",
|
||||||
"@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@6.4.3",
|
"@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@7.1.0",
|
||||||
"@push.rocks/smartacme": "npm:@push.rocks/smartacme@^8.0.0",
|
"@push.rocks/smartacme": "npm:@push.rocks/smartacme@^9.5.0",
|
||||||
"@push.rocks/smartregistry": "npm:@push.rocks/smartregistry@^2.2.0",
|
"@push.rocks/smartregistry": "npm:@push.rocks/smartregistry@^2.9.2",
|
||||||
"@push.rocks/smartstorage": "npm:@push.rocks/smartstorage@^6.3.0",
|
"@push.rocks/smartstorage": "npm:@push.rocks/smartstorage@^6.5.1",
|
||||||
"@push.rocks/taskbuffer": "npm:@push.rocks/taskbuffer@^3.1.0",
|
"@push.rocks/taskbuffer": "npm:@push.rocks/taskbuffer@^8.0.2",
|
||||||
"@api.global/typedrequest-interfaces": "npm:@api.global/typedrequest-interfaces@^3.0.19",
|
"@api.global/typedrequest-interfaces": "npm:@api.global/typedrequest-interfaces@^3.0.19",
|
||||||
"@api.global/typedrequest": "npm:@api.global/typedrequest@^3.2.6",
|
"@api.global/typedrequest": "npm:@api.global/typedrequest@^3.3.1",
|
||||||
"@api.global/typedserver": "npm:@api.global/typedserver@^8.3.1",
|
"@api.global/typedserver": "npm:@api.global/typedserver@^8.4.6",
|
||||||
"@push.rocks/smartguard": "npm:@push.rocks/smartguard@^3.1.0",
|
"@push.rocks/smartguard": "npm:@push.rocks/smartguard@^3.1.0",
|
||||||
"@push.rocks/smartjwt": "npm:@push.rocks/smartjwt@^2.2.1",
|
"@push.rocks/smartjwt": "npm:@push.rocks/smartjwt@^2.2.2",
|
||||||
"@api.global/typedsocket": "npm:@api.global/typedsocket@^4.1.2",
|
"@api.global/typedsocket": "npm:@api.global/typedsocket@^4.1.3",
|
||||||
"@serve.zone/containerarchive": "npm:@serve.zone/containerarchive@^0.1.3"
|
"@serve.zone/containerarchive": "npm:@serve.zone/containerarchive@^0.1.3"
|
||||||
},
|
},
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/onebox",
|
"name": "@serve.zone/onebox",
|
||||||
"version": "1.24.2",
|
"version": "1.24.3",
|
||||||
"description": "Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers",
|
"description": "Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers",
|
||||||
"main": "mod.ts",
|
"main": "mod.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -55,15 +55,15 @@
|
|||||||
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34",
|
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedrequest-interfaces": "^3.0.19",
|
"@api.global/typedrequest-interfaces": "^3.0.19",
|
||||||
"@api.global/typedsocket": "^4.1.2",
|
"@api.global/typedsocket": "^4.1.3",
|
||||||
"@design.estate/dees-catalog": "^3.49.0",
|
"@design.estate/dees-catalog": "^3.81.0",
|
||||||
"@design.estate/dees-element": "^2.1.6",
|
"@design.estate/dees-element": "^2.2.4",
|
||||||
"@serve.zone/catalog": "^2.9.0"
|
"@serve.zone/catalog": "^2.12.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbundle": "^2.10.0",
|
"@git.zone/tsbundle": "^2.10.1",
|
||||||
"@git.zone/tsdeno": "^1.3.1",
|
"@git.zone/tsdeno": "^1.3.1",
|
||||||
"@git.zone/tswatch": "^3.3.2"
|
"@git.zone/tswatch": "^3.3.3"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
|||||||
Generated
+832
-976
File diff suppressed because it is too large
Load Diff
@@ -59,12 +59,14 @@ Install the released binary:
|
|||||||
curl -sSL https://code.foss.global/serve.zone/onebox/raw/branch/main/install.sh | sudo bash
|
curl -sSL https://code.foss.global/serve.zone/onebox/raw/branch/main/install.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Install through the npm package wrapper with pnpm:
|
For published wrapper builds, install with pnpm:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm add --global @serve.zone/onebox
|
pnpm add --global @serve.zone/onebox
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This repository currently marks the package as private; use the install script or a released wrapper package when available.
|
||||||
|
|
||||||
The package wrapper downloads the platform-specific binary during postinstall. Current release assets are named for Linux, macOS, and Windows on x64/ARM64 where available.
|
The package wrapper downloads the platform-specific binary during postinstall. Current release assets are named for Linux, macOS, and Windows on x64/ARM64 where available.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|||||||
@@ -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
|
* Ensure onebox network exists
|
||||||
*/
|
*/
|
||||||
|
|||||||
+18
-7
@@ -66,7 +66,7 @@ export class Onebox {
|
|||||||
this.registry = new RegistryManager({
|
this.registry = new RegistryManager({
|
||||||
dataDir: './.nogit/registry-data',
|
dataDir: './.nogit/registry-data',
|
||||||
port: 4000,
|
port: 4000,
|
||||||
baseUrl: 'localhost:5000',
|
baseUrl: 'localhost:3000',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize domain management
|
// Initialize domain management
|
||||||
@@ -232,23 +232,31 @@ export class Onebox {
|
|||||||
*/
|
*/
|
||||||
private async ensureDefaultUser(): Promise<void> {
|
private async ensureDefaultUser(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const adminUser = this.database.getUserByUsername('admin');
|
const adminUsername = Deno.env.get('ONEBOX_ADMIN_USERNAME') || 'admin';
|
||||||
|
const adminUser = this.database.getUserByUsername(adminUsername);
|
||||||
|
|
||||||
if (!adminUser) {
|
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({
|
await this.database.createUser({
|
||||||
username: 'admin',
|
username: adminUsername,
|
||||||
passwordHash,
|
passwordHash,
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.warn('Default admin user created with username: admin, password: admin');
|
if (configuredPassword) {
|
||||||
logger.warn('IMPORTANT: Change the default password immediately!');
|
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) {
|
} catch (error) {
|
||||||
logger.error(`Failed to create default user: ${getErrorMessage(error)}`);
|
logger.error(`Failed to create default user: ${getErrorMessage(error)}`);
|
||||||
@@ -454,6 +462,9 @@ export class Onebox {
|
|||||||
// Close backup archive
|
// Close backup archive
|
||||||
await this.backupManager.close();
|
await this.backupManager.close();
|
||||||
|
|
||||||
|
// Release Docker client resources after all Docker-backed managers stopped.
|
||||||
|
await this.docker.stop();
|
||||||
|
|
||||||
// Close database
|
// Close database
|
||||||
this.database.close();
|
this.database.close();
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ export class ProxyLogReceiver {
|
|||||||
private port: number;
|
private port: number;
|
||||||
private running = false;
|
private running = false;
|
||||||
private connections: Set<Deno.TcpConn> = new Set();
|
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
|
// Adaptive sampling state
|
||||||
private logCountWindow: number[] = []; // timestamps of recent logs
|
private logCountWindow: number[] = []; // timestamps of recent logs
|
||||||
@@ -174,7 +177,7 @@ export class ProxyLogReceiver {
|
|||||||
logger.success(`ProxyLogReceiver started on TCP port ${this.port}`);
|
logger.success(`ProxyLogReceiver started on TCP port ${this.port}`);
|
||||||
|
|
||||||
// Start accepting connections in background
|
// Start accepting connections in background
|
||||||
this.acceptConnections();
|
this.acceptTask = this.acceptConnections();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to start ProxyLogReceiver: ${getErrorMessage(error)}`);
|
logger.error(`Failed to start ProxyLogReceiver: ${getErrorMessage(error)}`);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -190,7 +193,9 @@ export class ProxyLogReceiver {
|
|||||||
try {
|
try {
|
||||||
for await (const conn of this.server) {
|
for await (const conn of this.server) {
|
||||||
this.connections.add(conn);
|
this.connections.add(conn);
|
||||||
this.handleConnection(conn);
|
const handlerTask = this.handleConnection(conn);
|
||||||
|
this.connectionHandlers.add(handlerTask);
|
||||||
|
handlerTask.finally(() => this.connectionHandlers.delete(handlerTask));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.running) {
|
if (this.running) {
|
||||||
@@ -207,6 +212,7 @@ export class ProxyLogReceiver {
|
|||||||
logger.debug(`ProxyLogReceiver: Connection from ${remoteAddr.hostname}:${remoteAddr.port}`);
|
logger.debug(`ProxyLogReceiver: Connection from ${remoteAddr.hostname}:${remoteAddr.port}`);
|
||||||
|
|
||||||
const reader = conn.readable.getReader();
|
const reader = conn.readable.getReader();
|
||||||
|
this.connectionReaders.set(conn, reader);
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let buffer = '';
|
let buffer = '';
|
||||||
|
|
||||||
@@ -232,7 +238,13 @@ export class ProxyLogReceiver {
|
|||||||
logger.debug(`ProxyLogReceiver connection closed: ${getErrorMessage(error)}`);
|
logger.debug(`ProxyLogReceiver connection closed: ${getErrorMessage(error)}`);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
this.connectionReaders.delete(conn);
|
||||||
this.connections.delete(conn);
|
this.connections.delete(conn);
|
||||||
|
try {
|
||||||
|
reader.releaseLock();
|
||||||
|
} catch {
|
||||||
|
// Reader may already be released after cancellation during shutdown.
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
conn.close();
|
conn.close();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -447,6 +459,11 @@ export class ProxyLogReceiver {
|
|||||||
|
|
||||||
this.running = false;
|
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
|
// Close all connections
|
||||||
for (const conn of this.connections) {
|
for (const conn of this.connections) {
|
||||||
try {
|
try {
|
||||||
@@ -467,6 +484,15 @@ export class ProxyLogReceiver {
|
|||||||
this.server = null;
|
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
|
// Clear clients
|
||||||
this.clients.clear();
|
this.clients.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export class RegistryManager {
|
|||||||
},
|
},
|
||||||
ociTokens: {
|
ociTokens: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
realm: 'http://localhost:3000/v2/token',
|
realm: `http://${this.baseUrl}/v2/token`,
|
||||||
service: 'onebox-registry',
|
service: 'onebox-registry',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -233,13 +233,13 @@ export class SmartProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.ensureDockerClient();
|
||||||
|
|
||||||
if (!this.serviceRunning && !(await this.getExistingService())) {
|
if (!this.serviceRunning && !(await this.getExistingService())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await this.ensureDockerClient();
|
|
||||||
|
|
||||||
logger.info('Stopping SmartProxy service...');
|
logger.info('Stopping SmartProxy service...');
|
||||||
await this.removeService();
|
await this.removeService();
|
||||||
|
|
||||||
@@ -247,6 +247,16 @@ export class SmartProxyManager {
|
|||||||
logger.info('SmartProxy service stopped');
|
logger.info('SmartProxy service stopped');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to stop SmartProxy: ${getErrorMessage(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ export class OpsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private registerCustomRoutes(): void {
|
private registerCustomRoutes(): void {
|
||||||
|
this.server.typedserver.addRoute(
|
||||||
|
'/v2',
|
||||||
|
'ALL',
|
||||||
|
async (ctx) => this.oneboxRef.registry.handleRequest(ctx.request),
|
||||||
|
);
|
||||||
|
this.server.typedserver.addRoute(
|
||||||
|
'/v2/*',
|
||||||
|
'ALL',
|
||||||
|
async (ctx) => this.oneboxRef.registry.handleRequest(ctx.request),
|
||||||
|
);
|
||||||
this.server.typedserver.addRoute(
|
this.server.typedserver.addRoute(
|
||||||
'/backups/:backupId/download',
|
'/backups/:backupId/download',
|
||||||
'GET',
|
'GET',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user