feat(systemd): replace smartdaemon-based service management with native systemd commands
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-16 - 1.15.0 - feat(systemd)
|
||||
replace smartdaemon-based service management with native systemd commands
|
||||
|
||||
- adds a dedicated OneboxSystemd manager for enabling, disabling, starting, stopping, checking status, and following logs
|
||||
- introduces a new `onebox systemd` CLI command set and updates install and help output to use it
|
||||
- removes the smartdaemon dependency and related service management code
|
||||
|
||||
## 2026-03-16 - 1.14.10 - fix(services)
|
||||
stop auto-update monitoring during shutdown
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"@std/assert": "jsr:@std/assert@^1.0.15",
|
||||
"@std/encoding": "jsr:@std/encoding@^1.0.10",
|
||||
"@db/sqlite": "jsr:@db/sqlite@0.12.0",
|
||||
"@push.rocks/smartdaemon": "npm:@push.rocks/smartdaemon@^2.1.0",
|
||||
"@apiclient.xyz/docker": "npm:@apiclient.xyz/docker@^5.1.1",
|
||||
"@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@6.4.3",
|
||||
"@push.rocks/smartacme": "npm:@push.rocks/smartacme@^8.0.0",
|
||||
|
||||
12
install.sh
12
install.sh
@@ -23,7 +23,7 @@ SPECIFIED_VERSION=""
|
||||
INSTALL_DIR="/opt/onebox"
|
||||
GITEA_BASE_URL="https://code.foss.global"
|
||||
GITEA_REPO="serve.zone/onebox"
|
||||
SERVICE_NAME="smartdaemon_onebox"
|
||||
SERVICE_NAME="onebox"
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -276,7 +276,7 @@ if [ -f "/var/lib/onebox/onebox.db" ]; then
|
||||
if [ $SERVICE_WAS_RUNNING -eq 1 ]; then
|
||||
echo "The service has been restarted with your current settings."
|
||||
else
|
||||
echo "Start the service with: onebox daemon start"
|
||||
echo "Start the service with: onebox systemd start"
|
||||
fi
|
||||
else
|
||||
echo "Get started:"
|
||||
@@ -293,11 +293,11 @@ else
|
||||
echo " 2. Configure ACME email:"
|
||||
echo " onebox config set acmeEmail <your@email.com>"
|
||||
echo ""
|
||||
echo " 3. Install daemon:"
|
||||
echo " onebox daemon install"
|
||||
echo " 3. Enable systemd service:"
|
||||
echo " onebox systemd enable"
|
||||
echo ""
|
||||
echo " 4. Start daemon:"
|
||||
echo " onebox daemon start"
|
||||
echo " 4. Start service:"
|
||||
echo " onebox systemd start"
|
||||
echo ""
|
||||
echo " 5. Deploy your first service:"
|
||||
echo " onebox service add myapp --image nginx:latest --domain app.example.com"
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/onebox',
|
||||
version: '1.14.10',
|
||||
version: '1.15.0',
|
||||
description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers'
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
* Handles background monitoring, metrics collection, and automatic tasks
|
||||
*/
|
||||
|
||||
import * as plugins from '../plugins.ts';
|
||||
import { logger } from '../logging.ts';
|
||||
import { projectInfo } from '../info.ts';
|
||||
import { getErrorMessage } from '../utils/error.ts';
|
||||
import type { Onebox } from './onebox.ts';
|
||||
|
||||
@@ -18,7 +16,6 @@ const FALLBACK_PID_FILE = `${FALLBACK_PID_DIR}/onebox.pid`;
|
||||
|
||||
export class OneboxDaemon {
|
||||
private oneboxRef: Onebox;
|
||||
private smartdaemon: plugins.smartdaemon.SmartDaemon | null = null;
|
||||
private running = false;
|
||||
private monitoringInterval: number | null = null;
|
||||
private statsInterval: number | null = null;
|
||||
@@ -46,68 +43,6 @@ export class OneboxDaemon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install systemd service
|
||||
*/
|
||||
async installService(): Promise<void> {
|
||||
try {
|
||||
logger.info('Installing Onebox daemon service...');
|
||||
|
||||
// Initialize smartdaemon if needed
|
||||
if (!this.smartdaemon) {
|
||||
this.smartdaemon = new plugins.smartdaemon.SmartDaemon();
|
||||
}
|
||||
|
||||
// Get installation directory
|
||||
const execPath = Deno.execPath();
|
||||
|
||||
const service = await this.smartdaemon.addService({
|
||||
name: 'onebox',
|
||||
version: projectInfo.version,
|
||||
command: `${execPath} run --allow-all ${Deno.cwd()}/mod.ts daemon start`,
|
||||
description: 'Onebox - Self-hosted container platform',
|
||||
workingDir: Deno.cwd(),
|
||||
});
|
||||
|
||||
await service.save();
|
||||
await service.enable();
|
||||
|
||||
logger.success('Onebox daemon service installed');
|
||||
logger.info('Start with: sudo systemctl start smartdaemon_onebox');
|
||||
} catch (error) {
|
||||
logger.error(`Failed to install daemon service: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall systemd service
|
||||
*/
|
||||
async uninstallService(): Promise<void> {
|
||||
try {
|
||||
logger.info('Uninstalling Onebox daemon service...');
|
||||
|
||||
// Initialize smartdaemon if needed
|
||||
if (!this.smartdaemon) {
|
||||
this.smartdaemon = new plugins.smartdaemon.SmartDaemon();
|
||||
}
|
||||
|
||||
const services = await this.smartdaemon.systemdManager.getServices();
|
||||
const service = services.find(s => s.name === 'onebox');
|
||||
|
||||
if (service) {
|
||||
await service.stop();
|
||||
await service.disable();
|
||||
await service.delete();
|
||||
}
|
||||
|
||||
logger.success('Onebox daemon service uninstalled');
|
||||
} catch (error) {
|
||||
logger.error(`Failed to uninstall daemon service: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start daemon mode (background monitoring)
|
||||
*/
|
||||
@@ -482,36 +417,7 @@ export class OneboxDaemon {
|
||||
static async ensureNoDaemon(): Promise<void> {
|
||||
const running = await OneboxDaemon.isDaemonRunning();
|
||||
if (running) {
|
||||
throw new Error('Daemon is already running. Please stop it first with: onebox daemon stop');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get service status from systemd
|
||||
*/
|
||||
async getServiceStatus(): Promise<string> {
|
||||
try {
|
||||
// Don't need smartdaemon to check status, just use systemctl directly
|
||||
const command = new Deno.Command('systemctl', {
|
||||
args: ['status', 'smartdaemon_onebox'],
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const { code, stdout } = await command.output();
|
||||
const output = new TextDecoder().decode(stdout);
|
||||
|
||||
if (code === 0 || output.includes('active (running)')) {
|
||||
return 'running';
|
||||
} else if (output.includes('inactive') || output.includes('dead')) {
|
||||
return 'stopped';
|
||||
} else if (output.includes('failed')) {
|
||||
return 'failed';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
} catch (error) {
|
||||
return 'not-installed';
|
||||
throw new Error('Daemon is already running. Please stop it first with: onebox systemd stop');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { OneboxReverseProxy } from './reverseproxy.ts';
|
||||
import { OneboxDnsManager } from './dns.ts';
|
||||
import { OneboxSslManager } from './ssl.ts';
|
||||
import { OneboxDaemon } from './daemon.ts';
|
||||
import { OneboxSystemd } from './systemd.ts';
|
||||
import { OneboxHttpServer } from './httpserver.ts';
|
||||
import { CloudflareDomainSync } from './cloudflare-sync.ts';
|
||||
import { CertRequirementManager } from './cert-requirement-manager.ts';
|
||||
@@ -33,6 +34,7 @@ export class Onebox {
|
||||
public dns: OneboxDnsManager;
|
||||
public ssl: OneboxSslManager;
|
||||
public daemon: OneboxDaemon;
|
||||
public systemd: OneboxSystemd;
|
||||
public httpServer: OneboxHttpServer;
|
||||
public cloudflareDomainSync: CloudflareDomainSync;
|
||||
public certRequirementManager: CertRequirementManager;
|
||||
@@ -57,6 +59,7 @@ export class Onebox {
|
||||
this.dns = new OneboxDnsManager(this);
|
||||
this.ssl = new OneboxSslManager(this);
|
||||
this.daemon = new OneboxDaemon(this);
|
||||
this.systemd = new OneboxSystemd();
|
||||
this.httpServer = new OneboxHttpServer(this);
|
||||
this.registry = new RegistryManager({
|
||||
dataDir: './.nogit/registry-data',
|
||||
@@ -376,20 +379,6 @@ export class Onebox {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start daemon mode
|
||||
*/
|
||||
async startDaemon(): Promise<void> {
|
||||
await this.daemon.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop daemon mode
|
||||
*/
|
||||
async stopDaemon(): Promise<void> {
|
||||
await this.daemon.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start OpsServer (TypedRequest-based, serves new UI)
|
||||
*/
|
||||
|
||||
185
ts/classes/systemd.ts
Normal file
185
ts/classes/systemd.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Systemd Service Manager for Onebox
|
||||
*
|
||||
* Handles systemd unit file installation, enabling, starting, stopping,
|
||||
* and status checking. Modeled on nupst's direct systemctl approach —
|
||||
* no external library dependencies.
|
||||
*/
|
||||
|
||||
import { logger } from '../logging.ts';
|
||||
import { getErrorMessage } from '../utils/error.ts';
|
||||
|
||||
const SERVICE_NAME = 'onebox';
|
||||
const SERVICE_FILE_PATH = '/etc/systemd/system/onebox.service';
|
||||
|
||||
const SERVICE_UNIT_TEMPLATE = `[Unit]
|
||||
Description=Onebox - Self-hosted container platform
|
||||
After=network-online.target docker.service
|
||||
Wants=network-online.target
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/onebox systemd start-daemon
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
WorkingDirectory=/var/lib/onebox
|
||||
Environment=PATH=/usr/bin:/usr/local/bin
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`;
|
||||
|
||||
export class OneboxSystemd {
|
||||
/**
|
||||
* Install and enable the systemd service
|
||||
*/
|
||||
async enable(): Promise<void> {
|
||||
try {
|
||||
// Write the unit file
|
||||
logger.info('Writing systemd unit file...');
|
||||
await Deno.writeTextFile(SERVICE_FILE_PATH, SERVICE_UNIT_TEMPLATE);
|
||||
logger.info(`Unit file written to ${SERVICE_FILE_PATH}`);
|
||||
|
||||
// Reload systemd daemon
|
||||
await this.runSystemctl(['daemon-reload']);
|
||||
|
||||
// Enable the service
|
||||
const result = await this.runSystemctl(['enable', `${SERVICE_NAME}.service`]);
|
||||
if (!result.success) {
|
||||
throw new Error(`Failed to enable service: ${result.stderr}`);
|
||||
}
|
||||
|
||||
logger.success('Onebox systemd service enabled');
|
||||
logger.info('Start with: onebox systemd start');
|
||||
} catch (error) {
|
||||
logger.error(`Failed to enable service: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop, disable, and remove the systemd service
|
||||
*/
|
||||
async disable(): Promise<void> {
|
||||
try {
|
||||
// Stop the service (ignore errors if not running)
|
||||
await this.runSystemctl(['stop', `${SERVICE_NAME}.service`]);
|
||||
|
||||
// Disable the service
|
||||
await this.runSystemctl(['disable', `${SERVICE_NAME}.service`]);
|
||||
|
||||
// Remove the unit file
|
||||
try {
|
||||
await Deno.remove(SERVICE_FILE_PATH);
|
||||
logger.info(`Removed ${SERVICE_FILE_PATH}`);
|
||||
} catch {
|
||||
// File might not exist
|
||||
}
|
||||
|
||||
// Reload systemd daemon
|
||||
await this.runSystemctl(['daemon-reload']);
|
||||
|
||||
logger.success('Onebox systemd service disabled and removed');
|
||||
} catch (error) {
|
||||
logger.error(`Failed to disable service: ${getErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the service via systemctl
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
const result = await this.runSystemctl(['start', `${SERVICE_NAME}.service`]);
|
||||
if (!result.success) {
|
||||
logger.error(`Failed to start service: ${result.stderr}`);
|
||||
throw new Error(`Failed to start onebox service`);
|
||||
}
|
||||
logger.success('Onebox service started');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the service via systemctl
|
||||
*/
|
||||
async stop(): Promise<void> {
|
||||
const result = await this.runSystemctl(['stop', `${SERVICE_NAME}.service`]);
|
||||
if (!result.success) {
|
||||
logger.error(`Failed to stop service: ${result.stderr}`);
|
||||
throw new Error(`Failed to stop onebox service`);
|
||||
}
|
||||
logger.success('Onebox service stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and display service status
|
||||
*/
|
||||
async getStatus(): Promise<string> {
|
||||
const result = await this.runSystemctl(['status', `${SERVICE_NAME}.service`]);
|
||||
const output = result.stdout;
|
||||
|
||||
let status: string;
|
||||
if (output.includes('active (running)')) {
|
||||
status = 'running';
|
||||
} else if (output.includes('inactive') || output.includes('dead')) {
|
||||
status = 'stopped';
|
||||
} else if (output.includes('failed')) {
|
||||
status = 'failed';
|
||||
} else if (!result.success && result.stderr.includes('could not be found')) {
|
||||
status = 'not-installed';
|
||||
} else {
|
||||
status = 'unknown';
|
||||
}
|
||||
|
||||
// Print the raw systemctl output for full details
|
||||
if (output.trim()) {
|
||||
console.log(output);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show service logs via journalctl
|
||||
*/
|
||||
async showLogs(): Promise<void> {
|
||||
const cmd = new Deno.Command('journalctl', {
|
||||
args: ['-u', `${SERVICE_NAME}.service`, '-f'],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
});
|
||||
await cmd.output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the service unit file is installed
|
||||
*/
|
||||
async isInstalled(): Promise<boolean> {
|
||||
try {
|
||||
await Deno.stat(SERVICE_FILE_PATH);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a systemctl command and return results
|
||||
*/
|
||||
private async runSystemctl(
|
||||
args: string[]
|
||||
): Promise<{ success: boolean; stdout: string; stderr: string }> {
|
||||
const cmd = new Deno.Command('systemctl', {
|
||||
args,
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const result = await cmd.output();
|
||||
return {
|
||||
success: result.success,
|
||||
stdout: new TextDecoder().decode(result.stdout),
|
||||
stderr: new TextDecoder().decode(result.stderr),
|
||||
};
|
||||
}
|
||||
}
|
||||
89
ts/cli.ts
89
ts/cli.ts
@@ -7,6 +7,7 @@ import { projectInfo } from './info.ts';
|
||||
import { getErrorMessage } from './utils/error.ts';
|
||||
import { Onebox } from './classes/onebox.ts';
|
||||
import { OneboxDaemon } from './classes/daemon.ts';
|
||||
import { OneboxSystemd } from './classes/systemd.ts';
|
||||
|
||||
export async function runCli(): Promise<void> {
|
||||
const args = Deno.args;
|
||||
@@ -25,6 +26,19 @@ export async function runCli(): Promise<void> {
|
||||
const subcommand = args[1];
|
||||
|
||||
try {
|
||||
// === LIGHTWEIGHT COMMANDS (no init()) ===
|
||||
if (command === 'systemd') {
|
||||
await handleSystemdCommand(subcommand, args.slice(2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === 'upgrade') {
|
||||
await handleUpgradeCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
// === HEAVY COMMANDS (require full init()) ===
|
||||
|
||||
// Server command has special handling (doesn't shut down)
|
||||
if (command === 'server') {
|
||||
const onebox = new Onebox();
|
||||
@@ -60,10 +74,6 @@ export async function runCli(): Promise<void> {
|
||||
await handleNginxCommand(onebox, subcommand, args.slice(2));
|
||||
break;
|
||||
|
||||
case 'daemon':
|
||||
await handleDaemonCommand(onebox, subcommand, args.slice(2));
|
||||
break;
|
||||
|
||||
case 'config':
|
||||
await handleConfigCommand(onebox, subcommand, args.slice(2));
|
||||
break;
|
||||
@@ -72,10 +82,6 @@ export async function runCli(): Promise<void> {
|
||||
await handleStatusCommand(onebox);
|
||||
break;
|
||||
|
||||
case 'upgrade':
|
||||
await handleUpgradeCommand();
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.error(`Unknown command: ${command}`);
|
||||
printHelp();
|
||||
@@ -282,7 +288,7 @@ async function handleServerCommand(onebox: Onebox, args: string[]) {
|
||||
await OneboxDaemon.ensureNoDaemon();
|
||||
} catch (error) {
|
||||
logger.error('Cannot start in ephemeral mode: Daemon is already running');
|
||||
logger.info('Stop the daemon first: onebox daemon stop');
|
||||
logger.info('Stop the daemon first: onebox systemd stop');
|
||||
logger.info('Or run without --ephemeral to use the existing daemon');
|
||||
Deno.exit(1);
|
||||
}
|
||||
@@ -326,39 +332,49 @@ async function handleServerCommand(onebox: Onebox, args: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Daemon commands
|
||||
async function handleDaemonCommand(onebox: Onebox, subcommand: string, _args: string[]) {
|
||||
// Systemd service commands (lightweight — no Onebox init)
|
||||
async function handleSystemdCommand(subcommand: string, _args: string[]) {
|
||||
const systemd = new OneboxSystemd();
|
||||
|
||||
switch (subcommand) {
|
||||
case 'install':
|
||||
await onebox.daemon.installService();
|
||||
case 'enable':
|
||||
await systemd.enable();
|
||||
break;
|
||||
|
||||
case 'disable':
|
||||
await systemd.disable();
|
||||
break;
|
||||
|
||||
case 'start':
|
||||
await onebox.startDaemon();
|
||||
await systemd.start();
|
||||
break;
|
||||
|
||||
case 'stop':
|
||||
await onebox.stopDaemon();
|
||||
await systemd.stop();
|
||||
break;
|
||||
|
||||
case 'logs': {
|
||||
const command = new Deno.Command('journalctl', {
|
||||
args: ['-u', 'smartdaemon_onebox', '-f'],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
});
|
||||
await command.output();
|
||||
case 'status': {
|
||||
const status = await systemd.getStatus();
|
||||
logger.info(`Service status: ${status}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'status': {
|
||||
const status = await onebox.daemon.getServiceStatus();
|
||||
logger.info(`Daemon status: ${status}`);
|
||||
case 'logs':
|
||||
await systemd.showLogs();
|
||||
break;
|
||||
|
||||
case 'start-daemon': {
|
||||
// This is what systemd's ExecStart calls — full init + daemon loop
|
||||
const onebox = new Onebox();
|
||||
await onebox.init();
|
||||
await onebox.daemon.start();
|
||||
// start() blocks (keepAlive loop) until SIGTERM/SIGINT
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
logger.error(`Unknown daemon subcommand: ${subcommand}`);
|
||||
logger.error(`Unknown systemd subcommand: ${subcommand}`);
|
||||
logger.info('Available: enable, disable, start, stop, status, logs');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,11 +522,12 @@ Commands:
|
||||
nginx test
|
||||
nginx status
|
||||
|
||||
daemon install
|
||||
daemon start
|
||||
daemon stop
|
||||
daemon logs
|
||||
daemon status
|
||||
systemd enable Install and enable systemd service
|
||||
systemd disable Stop, disable, and remove systemd service
|
||||
systemd start Start onebox via systemctl
|
||||
systemd stop Stop onebox via systemctl
|
||||
systemd status Show systemd service status
|
||||
systemd logs Follow service logs (journalctl)
|
||||
|
||||
config show
|
||||
config set <key> <value>
|
||||
@@ -530,15 +547,15 @@ Development Workflow:
|
||||
onebox service add ... # In another terminal
|
||||
|
||||
Production Workflow:
|
||||
onebox daemon install # Install systemd service
|
||||
onebox daemon start # Start daemon
|
||||
onebox service add ... # CLI uses daemon
|
||||
onebox systemd enable # Install and enable systemd service
|
||||
onebox systemd start # Start via systemctl
|
||||
onebox service add ... # CLI manages services
|
||||
|
||||
Examples:
|
||||
onebox server --ephemeral # Start dev server
|
||||
onebox service add myapp --image nginx:latest --domain app.example.com --port 80
|
||||
onebox registry add --url registry.example.com --username user --password pass
|
||||
onebox daemon install
|
||||
onebox daemon start
|
||||
onebox systemd enable
|
||||
onebox systemd start
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export { OneboxReverseProxy } from './classes/reverseproxy.ts';
|
||||
export { OneboxDnsManager } from './classes/dns.ts';
|
||||
export { OneboxSslManager } from './classes/ssl.ts';
|
||||
export { OneboxDaemon } from './classes/daemon.ts';
|
||||
export { OneboxSystemd } from './classes/systemd.ts';
|
||||
export { OneboxHttpServer } from './classes/httpserver.ts';
|
||||
export { OneboxApiClient } from './classes/apiclient.ts';
|
||||
|
||||
|
||||
@@ -17,10 +17,6 @@ export { path, fs, http, encoding };
|
||||
import { Database } from '@db/sqlite';
|
||||
export const sqlite = { DB: Database };
|
||||
|
||||
// Systemd Daemon Integration
|
||||
import * as smartdaemon from '@push.rocks/smartdaemon';
|
||||
export { smartdaemon };
|
||||
|
||||
// Docker API Client
|
||||
import { DockerHost } from '@apiclient.xyz/docker';
|
||||
export const docker = { Docker: DockerHost };
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/onebox',
|
||||
version: '1.14.10',
|
||||
version: '1.15.0',
|
||||
description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user