feat(daemon): add automatic update mechanism (Updater), switch to system journal logs, and expose update controls in the UI
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
import { ProcessManager } from './process-manager.ts';
|
||||
import { SystemInfo } from './system-info.ts';
|
||||
import { Updater } from './updater.ts';
|
||||
import { UIServer } from '../ui/server.ts';
|
||||
import { runCommand } from '../utils/command.ts';
|
||||
import { VERSION } from '../version.ts';
|
||||
@@ -28,12 +29,14 @@ export class EcoDaemon {
|
||||
private config: DaemonConfig;
|
||||
private processManager: ProcessManager;
|
||||
private systemInfo: SystemInfo;
|
||||
private updater: Updater;
|
||||
private uiServer: UIServer;
|
||||
private logs: string[] = [];
|
||||
private serialLogs: string[] = [];
|
||||
private systemLogs: string[] = [];
|
||||
private swayStatus: ServiceStatus = { state: 'stopped' };
|
||||
private chromiumStatus: ServiceStatus = { state: 'stopped' };
|
||||
private manualRestartUntil: number = 0; // Timestamp until which auto-restart is disabled
|
||||
private lastAutoUpgradeCheck: number = 0; // Timestamp of last auto-upgrade check
|
||||
|
||||
constructor(config?: Partial<DaemonConfig>) {
|
||||
this.config = {
|
||||
@@ -45,6 +48,7 @@ export class EcoDaemon {
|
||||
|
||||
this.processManager = new ProcessManager(this.config.user);
|
||||
this.systemInfo = new SystemInfo();
|
||||
this.updater = new Updater((msg) => this.log(msg));
|
||||
this.uiServer = new UIServer(this.config.uiPort, this);
|
||||
}
|
||||
|
||||
@@ -64,8 +68,8 @@ export class EcoDaemon {
|
||||
return [...this.logs];
|
||||
}
|
||||
|
||||
getSerialLogs(): string[] {
|
||||
return [...this.serialLogs];
|
||||
getSystemLogs(): string[] {
|
||||
return [...this.systemLogs];
|
||||
}
|
||||
|
||||
async getStatus(): Promise<Record<string, unknown>> {
|
||||
@@ -78,7 +82,7 @@ export class EcoDaemon {
|
||||
chromiumStatus: this.chromiumStatus,
|
||||
systemInfo,
|
||||
logs: this.logs.slice(-50),
|
||||
serialLogs: this.serialLogs.slice(-50),
|
||||
systemLogs: this.systemLogs.slice(-50),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,6 +135,18 @@ export class EcoDaemon {
|
||||
}
|
||||
}
|
||||
|
||||
async getUpdateInfo(): Promise<unknown> {
|
||||
return this.updater.getUpdateInfo();
|
||||
}
|
||||
|
||||
async checkForUpdates(): Promise<void> {
|
||||
await this.updater.checkForUpdates();
|
||||
}
|
||||
|
||||
async upgradeToVersion(version: string): Promise<{ success: boolean; message: string }> {
|
||||
return this.updater.upgradeToVersion(version);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
this.log('EcoOS Daemon starting...');
|
||||
|
||||
@@ -139,8 +155,11 @@ export class EcoDaemon {
|
||||
await this.uiServer.start();
|
||||
this.log('Management UI started successfully');
|
||||
|
||||
// Start serial console reader in the background
|
||||
this.startSerialReader();
|
||||
// Start system journal reader in the background
|
||||
this.startJournalReader();
|
||||
|
||||
// Check for updates on startup
|
||||
this.updater.checkForUpdates().catch((e) => this.log(`Initial update check failed: ${e}`));
|
||||
|
||||
// Start the Sway/Chromium initialization in the background
|
||||
// This allows the UI server to remain responsive even if Sway fails
|
||||
@@ -313,12 +332,19 @@ export class EcoDaemon {
|
||||
return parseInt(result.stdout.trim(), 10);
|
||||
}
|
||||
|
||||
private startSerialReader(): void {
|
||||
private startJournalReader(): void {
|
||||
(async () => {
|
||||
try {
|
||||
const file = await Deno.open('/dev/ttyS0', { read: true });
|
||||
this.log('Serial console reader started on /dev/ttyS0');
|
||||
const reader = file.readable.getReader();
|
||||
const cmd = new Deno.Command('journalctl', {
|
||||
args: ['-f', '--no-pager', '-n', '100', '-o', 'short-iso'],
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const process = cmd.spawn();
|
||||
this.log('System journal reader started');
|
||||
|
||||
const reader = process.stdout.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (true) {
|
||||
@@ -326,14 +352,14 @@ export class EcoDaemon {
|
||||
if (done) break;
|
||||
const text = decoder.decode(value);
|
||||
for (const line of text.split('\n').filter((l) => l.trim())) {
|
||||
this.serialLogs.push(line);
|
||||
if (this.serialLogs.length > 1000) {
|
||||
this.serialLogs = this.serialLogs.slice(-1000);
|
||||
this.systemLogs.push(line);
|
||||
if (this.systemLogs.length > 1000) {
|
||||
this.systemLogs = this.systemLogs.slice(-1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.log(`Serial reader not available: ${error}`);
|
||||
this.log(`Journal reader not available: ${error}`);
|
||||
}
|
||||
})();
|
||||
}
|
||||
@@ -383,6 +409,16 @@ export class EcoDaemon {
|
||||
await this.tryStartSwayAndChromium();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for auto-upgrades every hour
|
||||
const now = Date.now();
|
||||
const oneHour = 60 * 60 * 1000;
|
||||
if (now - this.lastAutoUpgradeCheck > oneHour) {
|
||||
this.lastAutoUpgradeCheck = now;
|
||||
this.updater.checkAutoUpgrade().catch((e) =>
|
||||
this.log(`Auto-upgrade check failed: ${e}`)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.log(`Error in monitoring loop: ${error}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user