mirror of
https://github.com/everoddandeven/monerod-gui.git
synced 2024-12-31 16:09:53 +00:00
Version component
This commit is contained in:
parent
91bb85e119
commit
9b28d60acf
17 changed files with 254 additions and 22 deletions
33
app/main.ts
33
app/main.ts
|
@ -10,6 +10,10 @@ let win: BrowserWindow | null = null;
|
|||
const args = process.argv.slice(1),
|
||||
serve = args.some(val => val === '--serve');
|
||||
|
||||
function getMonerodPath(): string {
|
||||
return path.resolve(__dirname, monerodFilePath);
|
||||
}
|
||||
|
||||
function createWindow(): BrowserWindow {
|
||||
|
||||
const size = screen.getPrimaryDisplay().workAreaSize;
|
||||
|
@ -25,6 +29,7 @@ function createWindow(): BrowserWindow {
|
|||
allowRunningInsecureContent: (serve),
|
||||
contextIsolation: false
|
||||
},
|
||||
icon: path.join(__dirname, 'assets/icons/favicon.ico')
|
||||
});
|
||||
|
||||
if (serve) {
|
||||
|
@ -96,8 +101,20 @@ function execMoneroDaemon(configFilePath: string): ChildProcess {
|
|||
return monerodProcess;
|
||||
}
|
||||
|
||||
function startMoneroDaemon(commandOptions: string[], logHandler?: (message: string) => void): ChildProcessWithoutNullStreams {
|
||||
const monerodPath = path.resolve(__dirname, monerodFilePath);
|
||||
function getMonerodVersion(monerodFilePath: string): void {
|
||||
const monerodProcess = spawn(getMonerodPath(), [ '--version' ]);
|
||||
|
||||
monerodProcess.stdout.on('data', (data) => {
|
||||
win?.webContents.send('on-monerod-version', `${data}`);
|
||||
})
|
||||
|
||||
monerodProcess.stderr.on('data', (data) => {
|
||||
win?.webContents.send('on-monerod-version-error', `${data}`);
|
||||
})
|
||||
}
|
||||
|
||||
function startMoneroDaemon(commandOptions: string[]): ChildProcessWithoutNullStreams {
|
||||
const monerodPath = getMonerodPath();
|
||||
|
||||
console.log("Starting monerod daemon with options: " + commandOptions.join(" "));
|
||||
|
||||
|
@ -106,14 +123,14 @@ function startMoneroDaemon(commandOptions: string[], logHandler?: (message: stri
|
|||
|
||||
// Gestisci l'output di stdout in streaming
|
||||
monerodProcess.stdout.on('data', (data) => {
|
||||
console.log(`monerod stdout: ${data}`);
|
||||
//console.log(`monerod stdout: ${data}`);
|
||||
win?.webContents.send('monero-stdout', `${data}`);
|
||||
// Puoi anche inviare i log all'interfaccia utente tramite IPC
|
||||
});
|
||||
|
||||
// Gestisci gli errori in stderr
|
||||
monerodProcess.stderr.on('data', (data) => {
|
||||
console.error(`monerod stderr: ${data}`);
|
||||
//console.error(`monerod stderr: ${data}`);
|
||||
win?.webContents.send('monero-stderr', `${data}`);
|
||||
});
|
||||
|
||||
|
@ -150,10 +167,14 @@ try {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.on('start-monerod', (event, configFilePath: string[], logHandler?: (message: string) => void) => {
|
||||
startMoneroDaemon(configFilePath, logHandler);
|
||||
ipcMain.on('start-monerod', (event, configFilePath: string[]) => {
|
||||
startMoneroDaemon(configFilePath);
|
||||
})
|
||||
|
||||
ipcMain.on('get-monerod-version', (event, configFilePath: string) => {
|
||||
getMonerodVersion(configFilePath);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
// Catch Error
|
||||
// throw e;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<app-navbar></app-navbar>
|
||||
<div class="d-flex">
|
||||
<div class="d-flex" style="min-height: 100%;">
|
||||
<app-sidebar [isDaemonRunning]="daemonRunning"></app-sidebar>
|
||||
<div class="col-md-10">
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
@ -79,6 +79,7 @@ import { resolve } from 'path';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class DaemonService {
|
||||
private readonly versionApiUrl: string = 'https://api.github.com/repos/monero-project/monero/releases/latest';
|
||||
private dbName = 'DaemonSettingsDB';
|
||||
private storeName = 'settingsStore';
|
||||
private openDbPromise: Promise<IDBPDatabase>;
|
||||
|
@ -185,6 +186,14 @@ export class DaemonService {
|
|||
await new Promise<void>(f => setTimeout(f, ms));
|
||||
}
|
||||
|
||||
private async get(uri: string): Promise<{[key: string]: any}> {
|
||||
return await firstValueFrom<{ [key: string]: any }>(this.httpClient.get(`${uri}`,this.headers));
|
||||
}
|
||||
|
||||
private async post(uri: string, params: {[key: string]: any} = {}): Promise<{[key: string]: any}> {
|
||||
return await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${uri}`, params, this.headers));
|
||||
}
|
||||
|
||||
private async callRpc(request: RPCRequest): Promise<{ [key: string]: any }> {
|
||||
try {
|
||||
let method: string = '';
|
||||
|
@ -196,7 +205,7 @@ export class DaemonService {
|
|||
method = request.method;
|
||||
}
|
||||
|
||||
const response = await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/${method}`, request.toDictionary(), this.headers));
|
||||
const response = await this.post(`${this.url}/${method}`, request.toDictionary());
|
||||
|
||||
if (response.error) {
|
||||
this.raiseRpcError(response.error);
|
||||
|
@ -460,10 +469,51 @@ export class DaemonService {
|
|||
return SyncInfo.parse(response.result);
|
||||
}
|
||||
|
||||
public async getVersion(): Promise<DaemonVersion> {
|
||||
const response = await this.callRpc(new GetVersionRequest());
|
||||
public async getLatestVersion(): Promise<DaemonVersion> {
|
||||
const response = await this.get(this.versionApiUrl);
|
||||
|
||||
return DaemonVersion.parse(response.result);
|
||||
if (typeof response.tag_name != 'string') {
|
||||
throw new Error("Could not get tag name version");
|
||||
}
|
||||
|
||||
if (typeof response.name != 'string') {
|
||||
throw new Error("Could not get name version");
|
||||
}
|
||||
|
||||
const nameComponents = response.name.split(",");
|
||||
|
||||
if (nameComponents.length == 0) {
|
||||
throw new Error("Could not get name");
|
||||
}
|
||||
|
||||
const name = nameComponents[0];
|
||||
|
||||
return new DaemonVersion(0, true, `Monero '${name}' (${response.tag_name}-release)`);
|
||||
}
|
||||
|
||||
public async getVersion(dontUseRpc: boolean = false): Promise<DaemonVersion> {
|
||||
if(!dontUseRpc && this.daemonRunning) {
|
||||
const response = await this.callRpc(new GetVersionRequest());
|
||||
|
||||
return DaemonVersion.parse(response.result);
|
||||
}
|
||||
else if (dontUseRpc) {
|
||||
const monerodPath: string = ''; // TO DO get local monerod path
|
||||
|
||||
return new Promise<DaemonVersion>((resolve, reject) => {
|
||||
this.electronService.ipcRenderer.on('on-monerod-version', (event, version: string) => {
|
||||
resolve(DaemonVersion.parse(version));
|
||||
});
|
||||
|
||||
this.electronService.ipcRenderer.on('on-monerod-version-error', (event, version: string) => {
|
||||
reject(version);
|
||||
});
|
||||
|
||||
this.electronService.ipcRenderer.send('get-monerod-version', monerodPath);
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error("Daemon not running");
|
||||
}
|
||||
|
||||
public async getFeeEstimate(): Promise<FeeEstimate> {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
|
||||
@for(card of cards; track card.header) {
|
||||
@if(card.loading) {
|
||||
@if(card.loading && !stoppingDaemon) {
|
||||
<div class="card text-bg-dark m-3 text-center" style="max-width: 18rem;" aria-hidden="true">
|
||||
<div class="card-header"><strong>{{card.header}}</strong></div>
|
||||
<div class="card-body">
|
||||
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
</div>
|
||||
}
|
||||
@else {
|
||||
@else if (!stoppingDaemon) {
|
||||
<div class="card text-bg-dark m-3 text-center" style="max-width: 18rem;">
|
||||
<div class="card-header"><strong>{{card.header}}</strong></div>
|
||||
<div class="card-body">
|
||||
|
|
|
@ -150,6 +150,8 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
console.error(error);
|
||||
this.daemonRunning = false;
|
||||
}
|
||||
|
||||
this.cards = this.createLoadingCards();
|
||||
|
||||
this.startingDaemon = false;
|
||||
}, 500);
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="terminal bg-dark text-light p-3 m-4">
|
||||
<div *ngIf="lines.length == 0" class="h-100 p-5 text-bg-dark rounded-3 m-4 text-center">
|
||||
<h2><i class="bi bi-exclamation-diamond"></i> No logs</h2>
|
||||
<p>Start monero daemon to enable session logging</p>
|
||||
</div>
|
||||
|
||||
<div *ngIf="lines.length > 0" class="terminal bg-dark text-light p-3 m-4" #logTerminal>
|
||||
<div class="terminal-output" id="terminalOutput">
|
||||
<ng-container *ngFor="let line of lines; trackBy: trackByFn">
|
||||
<div>{{ line }}</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AfterViewInit, Component, NgZone } from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, NgZone, ViewChild } from '@angular/core';
|
||||
import { LogsService } from './logs.service';
|
||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
|||
styleUrl: './logs.component.scss'
|
||||
})
|
||||
export class LogsComponent implements AfterViewInit {
|
||||
@ViewChild('logTerminal', { read: ElementRef }) public logTerminal?: ElementRef<any>;
|
||||
|
||||
constructor(private navbarService: NavbarService, private logsService: LogsService, private ngZone: NgZone) {
|
||||
this.logsService.onLog.subscribe((message: string) => this.onLog());
|
||||
|
@ -18,14 +19,16 @@ export class LogsComponent implements AfterViewInit {
|
|||
}
|
||||
|
||||
private onLog(): void {
|
||||
if (this.logTerminal) this.logTerminal.nativeElement.scrollTop = this.logTerminal.nativeElement.scrollHeight;
|
||||
// Scorri automaticamente in basso
|
||||
setTimeout(() => {
|
||||
this.ngZone.run(() => {
|
||||
this.lines;
|
||||
const terminalOutput = document.getElementById('terminalOutput');
|
||||
const terminalOutput = <HTMLDivElement | null>document.getElementById('terminalOutput');
|
||||
if (terminalOutput) {
|
||||
terminalOutput.style.width = `${window.innerWidth}`;
|
||||
terminalOutput.scrollTop = terminalOutput.scrollHeight;
|
||||
console.log(`scrolling from ${terminalOutput.offsetTop} to ${terminalOutput.scrollHeight}`)
|
||||
terminalOutput.scrollBy(0, terminalOutput.scrollHeight)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1 +1,27 @@
|
|||
<p>version works!</p>
|
||||
<div class="row d-flex">
|
||||
@for(card of cards; track card.header) {
|
||||
@if(card.loading) {
|
||||
<div class="card text-bg-dark m-3 text-center" style="max-width: 18rem;" aria-hidden="true">
|
||||
<div class="card-header"><strong>{{card.header}}</strong></div>
|
||||
<div class="card-body">
|
||||
|
||||
<p class="card-text placeholder-glow">
|
||||
<span class="placeholder col-7"></span>
|
||||
<span class="placeholder col-4"></span>
|
||||
<span class="placeholder col-4"></span>
|
||||
<span class="placeholder col-6"></span>
|
||||
<span class="placeholder col-8"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@else {
|
||||
<div class="card text-bg-dark m-3 text-center" style="max-width: 18rem;">
|
||||
<div class="card-header"><strong>{{card.header}}</strong></div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{card.content}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
|
@ -2,6 +2,8 @@ import { AfterViewInit, Component } from '@angular/core';
|
|||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||
import { DaemonService } from '../../core/services/daemon/daemon.service';
|
||||
import { SimpleBootstrapCard } from '../../shared/utils';
|
||||
import { DaemonVersion } from '../../../common/DaemonVersion';
|
||||
|
||||
@Component({
|
||||
selector: 'app-version',
|
||||
|
@ -10,14 +12,50 @@ import { DaemonService } from '../../core/services/daemon/daemon.service';
|
|||
})
|
||||
export class VersionComponent implements AfterViewInit {
|
||||
private readonly links: NavbarLink[];
|
||||
public cards: SimpleBootstrapCard[];
|
||||
public currentVersion?: DaemonVersion;
|
||||
public latestVersion?: DaemonVersion;
|
||||
|
||||
constructor(private navbarService: NavbarService, private daemonService: DaemonService) {
|
||||
this.links = [
|
||||
new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview')
|
||||
];
|
||||
this.cards = this.createCards();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.navbarService.setLinks(this.links);
|
||||
private createCards(): SimpleBootstrapCard[] {
|
||||
return [
|
||||
new SimpleBootstrapCard('Current version', this.currentVersion ? this.currentVersion.fullname : '', this.currentVersion == null),
|
||||
new SimpleBootstrapCard('Latest version', this.latestVersion ? this.latestVersion.fullname : '', this.latestVersion == null)
|
||||
];
|
||||
}
|
||||
|
||||
private createErrorCards(): SimpleBootstrapCard[] {
|
||||
return [
|
||||
new SimpleBootstrapCard('Current version', 'Error', false),
|
||||
new SimpleBootstrapCard('Latest version', 'Error', false)
|
||||
];
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
this.navbarService.setLinks(this.links);
|
||||
this.load()
|
||||
.then(() => {
|
||||
this.cards = this.createCards();
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.currentVersion = undefined;
|
||||
this.latestVersion = undefined
|
||||
this.cards = this.createErrorCards();
|
||||
});
|
||||
}
|
||||
|
||||
public async load(): Promise<void> {
|
||||
const version = await this.daemonService.getVersion(true);
|
||||
const latestVersion = await this.daemonService.getLatestVersion();
|
||||
|
||||
this.currentVersion = version;
|
||||
this.latestVersion = latestVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<nav class="navbar navbar-expand-lg d-flex justify-content-center py-3 text-bg-dark">
|
||||
|
||||
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
|
||||
<a href="" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
|
||||
<img class="m-1" src="/assets/icons/monero-symbol-on-white-480.png" width="40" height="40">
|
||||
<span class="fs-4"><strong>Monero Daemon</strong></span>
|
||||
</a>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
<div class="d-flex flex-column flex-shrink-0 p-3 text-bg-dark" style="width: 280px;">
|
||||
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
body {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
html {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.dropdown-toggle { outline: 0; }
|
||||
|
||||
.btn-toggle {
|
||||
padding: .25rem .5rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-emphasis-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-toggle:hover,
|
||||
.btn-toggle:focus {
|
||||
color: rgba(var(--bs-emphasis-color-rgb), .85);
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
}
|
||||
|
||||
.btn-toggle::before {
|
||||
width: 1.25em;
|
||||
line-height: 0;
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
transition: transform .35s ease;
|
||||
transform-origin: .5em 50%;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] .btn-toggle::before {
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28255,255,255,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.btn-toggle[aria-expanded="true"] {
|
||||
color: rgba(var(--bs-emphasis-color-rgb), .85);
|
||||
}
|
||||
.btn-toggle[aria-expanded="true"]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.btn-toggle-nav a {
|
||||
padding: .1875rem .5rem;
|
||||
margin-top: .125rem;
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
.btn-toggle-nav a:hover,
|
||||
.btn-toggle-nav a:focus {
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
}
|
||||
|
||||
.scrollarea {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
|
@ -32,6 +32,8 @@ export class SidebarComponent implements OnChanges {
|
|||
private createLightLinks(): NavLink[] {
|
||||
return [
|
||||
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
|
||||
new NavLink('Logs', '/logs', 'bi bi-terminal'),
|
||||
new NavLink('Version', '/version', 'bi bi-git'),
|
||||
new NavLink('Settings', '/settings', 'bi bi-gear')
|
||||
];
|
||||
}
|
||||
|
|
11
src/app/shared/utils/SimpleBootstrapCard.ts
Normal file
11
src/app/shared/utils/SimpleBootstrapCard.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export class SimpleBootstrapCard {
|
||||
public header: string;
|
||||
public content: string;
|
||||
public loading: boolean;
|
||||
|
||||
constructor(header: string, content: string, loading: boolean = false) {
|
||||
this.header = header;
|
||||
this.content = content;
|
||||
this.loading = loading;
|
||||
}
|
||||
}
|
1
src/app/shared/utils/index.ts
Normal file
1
src/app/shared/utils/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { SimpleBootstrapCard } from "./SimpleBootstrapCard";
|
|
@ -1,13 +1,19 @@
|
|||
export class DaemonVersion {
|
||||
public readonly version: number;
|
||||
public readonly release: boolean;
|
||||
public readonly fullname: string;
|
||||
|
||||
constructor(version: number, release: boolean) {
|
||||
constructor(version: number, release: boolean, fullname: string = '') {
|
||||
this.version = version;
|
||||
this.release = release;
|
||||
this.fullname = fullname;
|
||||
}
|
||||
|
||||
public static parse(version: any) {
|
||||
if (typeof version == 'string') {
|
||||
return new DaemonVersion(0, false, version);
|
||||
}
|
||||
|
||||
const v: number = version.version;
|
||||
const release: boolean = version.release;
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ html, body {
|
|||
padding: 0;
|
||||
background-color: #373636;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
html {
|
||||
|
|
Loading…
Reference in a new issue