mirror of
https://github.com/everoddandeven/monerod-gui.git
synced 2024-12-22 19:49:27 +00:00
Logs methods implementation, transactions methods implementation, network component implementation
This commit is contained in:
parent
91b38d1fb4
commit
e6cb2abcb0
18 changed files with 482 additions and 24 deletions
|
@ -27,6 +27,7 @@ import { SettingsModule } from './pages/settings/settings.module';
|
|||
import { LogsModule } from './pages/logs/logs.module';
|
||||
import { VersionModule } from './pages/version/version.module';
|
||||
import { HardForkInfoModule } from './pages/hard-fork-info/hard-fork-info.module';
|
||||
import { NetworkModule } from './pages/network/network.module';
|
||||
|
||||
// AoT requires an exported function for factories
|
||||
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||
|
@ -50,6 +51,7 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||
SettingsModule,
|
||||
HardForkInfoModule,
|
||||
VersionModule,
|
||||
NetworkModule,
|
||||
TranslateModule,
|
||||
AppRoutingModule,
|
||||
TranslateModule.forRoot({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { DaemonService } from './daemon.service';
|
||||
import { BlockCount, BlockHeader, Chain, DaemonInfo, SyncInfo } from '../../../../common';
|
||||
import { BlockCount, BlockHeader, Chain, DaemonInfo, NetStats, SyncInfo } from '../../../../common';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -34,6 +34,9 @@ export class DaemonDataService {
|
|||
private _altChains: Chain[] = [];
|
||||
private _gettingAltChains: boolean = false;
|
||||
|
||||
private _netStats?: NetStats;
|
||||
private _gettingNetStats: boolean = false;
|
||||
|
||||
public readonly syncStart: EventEmitter<void> = new EventEmitter<void>();
|
||||
public readonly syncEnd: EventEmitter<void> = new EventEmitter<void>();
|
||||
public readonly syncError: EventEmitter<Error> = new EventEmitter<Error>();
|
||||
|
@ -126,6 +129,14 @@ export class DaemonDataService {
|
|||
return this._gettingAltChains;
|
||||
}
|
||||
|
||||
public get netStats(): NetStats | undefined {
|
||||
return this.netStats;
|
||||
}
|
||||
|
||||
public get gettingNetStats(): boolean {
|
||||
return this._gettingNetStats;
|
||||
}
|
||||
|
||||
public setRefreshTimeout(ms: number = 5000): void {
|
||||
this.refreshTimeoutMs = ms;
|
||||
}
|
||||
|
@ -203,6 +214,10 @@ export class DaemonDataService {
|
|||
this._altChains = await this.daemonService.getAlternateChains();
|
||||
this._gettingAltChains = false;
|
||||
|
||||
this._gettingNetStats = true;
|
||||
this._netStats = await this.daemonService.getNetStats();
|
||||
this._gettingNetStats = false;
|
||||
|
||||
this._lastRefresh = Date.now();
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
|
@ -212,6 +227,7 @@ export class DaemonDataService {
|
|||
this._gettingLastBlockHeader = false;
|
||||
this._gettingIsBlockchainPruned = false;
|
||||
this._gettingAltChains = false;
|
||||
this._gettingNetStats = false;
|
||||
|
||||
this.syncError.emit(<Error>error);
|
||||
|
||||
|
|
|
@ -38,7 +38,10 @@ import {
|
|||
IsKeyImageSpentRequest,
|
||||
GetAltBlockHashesRequest,
|
||||
SaveBcRequest,
|
||||
SetBootstrapDaemonRequest
|
||||
SetBootstrapDaemonRequest,
|
||||
SetLogLevelRequest,
|
||||
SetLogHashRateRequest,
|
||||
SetLogCategoriesRequest
|
||||
} from '../../../../common/request';
|
||||
import { BlockTemplate } from '../../../../common/BlockTemplate';
|
||||
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
|
||||
|
@ -705,6 +708,10 @@ export class DaemonService {
|
|||
public async sendRawTransaction(txAsHex: string, doNotRelay: boolean = false): Promise<TxInfo> {
|
||||
const response = await this.callRpc(new SendRawTransactionRequest(txAsHex, doNotRelay));
|
||||
|
||||
if (typeof response.status == 'string' && response.status != 'OK') {
|
||||
throw new Error(response.reason);
|
||||
}
|
||||
|
||||
return TxInfo.parse(response);
|
||||
}
|
||||
|
||||
|
@ -836,6 +843,30 @@ export class DaemonService {
|
|||
return await this.update('download', path);
|
||||
}
|
||||
|
||||
public async setLogLevel(level: number): Promise<void> {
|
||||
const response = await this.callRpc(new SetLogLevelRequest(level));
|
||||
|
||||
if (response.status != 'OK') {
|
||||
throw new Error(response.status);
|
||||
}
|
||||
}
|
||||
|
||||
public async setLogCategories(cateogories: string): Promise<void> {
|
||||
const response = await this.callRpc(new SetLogCategoriesRequest(cateogories));
|
||||
|
||||
if (response.status != 'OK') {
|
||||
throw new Error(response.status);
|
||||
}
|
||||
}
|
||||
|
||||
public async setLogHashRate(visible: boolean): Promise<void> {
|
||||
const response = await this.callRpc(new SetLogHashRateRequest(visible));
|
||||
|
||||
if (response.status != 'OK') {
|
||||
throw new Error(response.status);
|
||||
}
|
||||
}
|
||||
|
||||
public getGuiVersion(): string {
|
||||
return "0.1.0-alpha";
|
||||
}
|
||||
|
|
|
@ -2,22 +2,110 @@
|
|||
<h1 class="h2">Logs</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<ul class="nav nav-pills m-3" id="pills-tab" role="tablist">
|
||||
<li class="nav-item mr-2" role="presentation">
|
||||
<button [class]="true ? 'nav-link active btn-sm' : 'nav-link btn-sm'" id="" data-bs-toggle="pill" [attr.data-bs-target]="" type="button" role="tab" [attr.aria-controls]="" [attr.aria-selected]="true" [disabled]="false">Overview</button>
|
||||
</li>
|
||||
@for(navbarLink of navbarLinks; track navbarLink.name) {
|
||||
<li class="nav-item mr-2" role="presentation">
|
||||
<button [class]="navbarLink.selected ? 'nav-link active btn-sm' : 'nav-link btn-sm'" [id]="navbarLink.id" data-bs-toggle="pill" [attr.data-bs-target]="navbarLink.target" type="button" role="tab" [attr.aria-controls]="navbarLink.controls" [attr.aria-selected]="navbarLink.selected" [disabled]="navbarLink.disabled">{{navbarLink.name}}</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<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 m-4"></i> No logs</h2>
|
||||
<p>Start monero daemon to enable session logging</p>
|
||||
</div>
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
|
||||
<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>
|
||||
</ng-container>
|
||||
<div class="tab-pane fade show active" id="pills-overview" role="tabpanel" aria-labelledby="pills-overview-tab" tabindex="0">
|
||||
<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 m-4"></i> No logs</h2>
|
||||
<p>Start monero daemon to enable session logging</p>
|
||||
</div>
|
||||
</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>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-set-log-level" role="tabpanel" aria-labelledby="pills-set-log-level-tab" tabindex="0">
|
||||
<div *ngIf="setLogLevelError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>
|
||||
<div>
|
||||
{{setLogLevelError}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="setLogLevelSuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-check-circle m-2"></i></h4>
|
||||
<div>
|
||||
Successfully set log level
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-5 p-2">
|
||||
<div class="col-md-7 col-lg-10">
|
||||
<h4 class="mb-3">Set the daemon log level</h4>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label for="set-log-level-level" class="form-label">Log Level</label>
|
||||
<select class="form-select" id="set-log-level-level" [(ngModel)]="setLogLevelLevel" [ngModelOptions]="{standalone: true}">
|
||||
<option [ngValue]="0" [selected]="setLogLevelLevel == 0">0</option>
|
||||
<option [ngValue]="1" [selected]="setLogLevelLevel == 1">1</option>
|
||||
<option [ngValue]="2" [selected]="setLogLevelLevel == 2">2</option>
|
||||
<option [ngValue]="3" [selected]="setLogLevelLevel == 3">3</option>
|
||||
<option [ngValue]="4" [selected]="setLogLevelLevel == 4">4</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<button *ngIf="!settingLogLevel" class="w-100 btn btn-primary btn-lg" type="button" (click)="setLogLevel()">Set Log Level</button>
|
||||
<button *ngIf="settingLogLevel" class="w-100 btn btn-primary btn-lg" type="button" disabled>Setting log level ...</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-set-log-categories" role="tabpanel" aria-labelledby="pills-set-log-categories-tab" tabindex="0">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-set-log-hash-rate" role="tabpanel" aria-labelledby="pills-set-log-hash-rate-tab" tabindex="0">
|
||||
<div *ngIf="setLogHashRateError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>
|
||||
<div>
|
||||
{{setLogHashRateError}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="setLogHashRateSuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-check-circle m-2"></i></h4>
|
||||
<div>
|
||||
Successfully set log hash rate
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-5 p-2">
|
||||
<div class="cold-md-7 col-lg-12">
|
||||
<h4 class="mb-3">Set the log hash rate display mode</h4>
|
||||
|
||||
<div class="row gy-3">
|
||||
<div class="form-check form-switch col-md-6">
|
||||
<label for="set-log-hash-rate-enabled" class="form-check-label">Enabled</label>
|
||||
<input class="form-control form-check-input" type="checkbox" role="switch" id="set-log-hash-rate-enabled" [checked]="setLogHashRateEnabled" [(ngModel)]="setLogHashRateEnabled" [ngModelOptions]="{standalone: true}">
|
||||
<br>
|
||||
<small class="text-body-secondary">States if hash rate logs should be visible or hidden</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<button *ngIf="!settingLogHashRate" class="w-100 btn btn-primary btn-lg" type="button" (click)="setLogHashRate()">Set Log Hash Rate</button>
|
||||
<button *ngIf="settingLogHashRate" class="w-100 btn btn-primary btn-lg" type="button" disabled>Setting Log Hash Rate ...</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -1,6 +1,8 @@
|
|||
import { AfterViewInit, Component, ElementRef, NgZone, ViewChild } from '@angular/core';
|
||||
import { LogsService } from './logs.service';
|
||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||
import { DaemonService } from '../../core/services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logs',
|
||||
|
@ -9,9 +11,31 @@ import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
|||
})
|
||||
export class LogsComponent implements AfterViewInit {
|
||||
@ViewChild('logTerminal', { read: ElementRef }) public logTerminal?: ElementRef<any>;
|
||||
public readonly navbarLinks: NavbarLink[];
|
||||
|
||||
public setLogLevelLevel: number = 0;
|
||||
public settingLogLevel: boolean = false;
|
||||
public setLogLevelError: string = '';
|
||||
public setLogLevelSuccess: boolean = false;
|
||||
|
||||
constructor(private navbarService: NavbarService, private logsService: LogsService, private ngZone: NgZone) {
|
||||
public setLogCategoriesCategories: string = '';
|
||||
public settingLogCategories: boolean = false;
|
||||
public setLogCategoriesError: string = '';
|
||||
public setLogCategoriesSuccess: boolean = false;
|
||||
|
||||
public setLogHashRateEnabled: boolean = false;
|
||||
public settingLogHashRate: boolean = false;
|
||||
public setLogHashRateError: string = '';
|
||||
public setLogHashRateSuccess: boolean = false;
|
||||
|
||||
constructor(private navbarService: NavbarService, private logsService: LogsService, private daemonService: DaemonService, private ngZone: NgZone) {
|
||||
this.logsService.onLog.subscribe((message: string) => this.onLog());
|
||||
this.navbarLinks = [
|
||||
new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview'),
|
||||
new NavbarLink('pills-set-log-level-tab', '#pills-set-log-level', 'pills-set-log-level', false, 'Set Log Level'),
|
||||
new NavbarLink('pills-set-log-categories-tab', '#pills-set-log-categories', 'pills-set-log-categories', false, 'Set Log Categories'),
|
||||
new NavbarLink('pills-set-log-hash-rate-tab', '#pills-set-log-hash-rate', 'pills-set-log-hash-rate', false, 'Set Log Hash Rate')
|
||||
];
|
||||
}
|
||||
|
||||
public get lines(): string[] {
|
||||
|
@ -42,10 +66,45 @@ export class LogsComponent implements AfterViewInit {
|
|||
return index; // usa l'indice per tracciare gli elementi
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.navbarService.removeLinks();
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
}, 500); }
|
||||
public ngAfterViewInit(): void {
|
||||
this.navbarService.removeLinks();
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
public async setLogLevel(): Promise<void> {
|
||||
this.settingLogLevel = true;
|
||||
|
||||
try {
|
||||
await this.daemonService.setLogLevel(this.setLogLevelLevel);
|
||||
|
||||
this.setLogLevelError = '';
|
||||
this.setLogLevelSuccess = true;
|
||||
} catch (error) {
|
||||
this.setLogLevelSuccess = false;
|
||||
this.setLogLevelError = `${error}`;
|
||||
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
this.settingLogLevel = false;
|
||||
}
|
||||
|
||||
public async setLogHashRate(): Promise<void> {
|
||||
this.settingLogHashRate = true;
|
||||
|
||||
try {
|
||||
await this.daemonService.setLogHashRate(this.setLogHashRateEnabled);
|
||||
this.setLogHashRateError = '';
|
||||
this.setLogHashRateSuccess = true;
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
this.setLogHashRateError = `${error}`;
|
||||
this.setLogHashRateSuccess = false;
|
||||
}
|
||||
|
||||
this.settingLogHashRate = false;
|
||||
}
|
||||
}
|
||||
|
|
18
src/app/pages/network/network-routing.module.ts
Normal file
18
src/app/pages/network/network-routing.module.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NetworkComponent } from './network.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'network',
|
||||
component: NetworkComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class NetworkRoutingModule { }
|
18
src/app/pages/network/network.component.html
Normal file
18
src/app/pages/network/network.component.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Network</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<ul class="nav nav-pills m-3" id="pills-tab" role="tablist">
|
||||
@for(navbarLink of navbarLinks; track navbarLink.name) {
|
||||
<li class="nav-item mr-2" role="presentation">
|
||||
<button [class]="navbarLink.selected ? 'nav-link active btn-sm' : 'nav-link btn-sm'" [id]="navbarLink.id" data-bs-toggle="pill" [attr.data-bs-target]="navbarLink.target" type="button" role="tab" [attr.aria-controls]="navbarLink.controls" [attr.aria-selected]="navbarLink.selected" [disabled]="navbarLink.disabled">{{navbarLink.name}}</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="daemonRunning" class="tab-content" id="pills-tabContent">
|
||||
</div>
|
||||
|
||||
<app-daemon-not-running></app-daemon-not-running>
|
||||
<app-daemon-stopping></app-daemon-stopping>
|
0
src/app/pages/network/network.component.scss
Normal file
0
src/app/pages/network/network.component.scss
Normal file
23
src/app/pages/network/network.component.spec.ts
Normal file
23
src/app/pages/network/network.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NetworkComponent } from './network.component';
|
||||
|
||||
describe('NetworkComponent', () => {
|
||||
let component: NetworkComponent;
|
||||
let fixture: ComponentFixture<NetworkComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NetworkComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NetworkComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
30
src/app/pages/network/network.component.ts
Normal file
30
src/app/pages/network/network.component.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { AfterViewInit, Component } from '@angular/core';
|
||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||
import { DaemonDataService } from '../../core/services';
|
||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-network',
|
||||
templateUrl: './network.component.html',
|
||||
styleUrl: './network.component.scss'
|
||||
})
|
||||
export class NetworkComponent implements AfterViewInit {
|
||||
public daemonRunning: boolean = false;
|
||||
public readonly navbarLinks: NavbarLink[];
|
||||
|
||||
constructor(private navbarService: NavbarService, private daemonData: DaemonDataService) {
|
||||
this.navbarLinks = [
|
||||
new NavbarLink('pills-net-stats-tab', '#pills-net-stats', 'pills-net-stats', false, 'Statistics'),
|
||||
new NavbarLink('pills-limits-tab', '#pills-limits', 'pills-limits', false, 'Limits'),
|
||||
new NavbarLink('pills-public-nodes-tab', '#pills-public-nodes', 'pills-public-nodes', false, 'Public Nodes')
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
this.daemonRunning = this.daemonData.running;
|
||||
|
||||
this.navbarService.setLinks(this.navbarLinks);
|
||||
}
|
||||
|
||||
}
|
17
src/app/pages/network/network.module.ts
Normal file
17
src/app/pages/network/network.module.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NetworkRoutingModule } from './network-routing.module';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { NetworkComponent } from './network.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [NetworkComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
NetworkRoutingModule
|
||||
]
|
||||
})
|
||||
export class NetworkModule { }
|
|
@ -12,6 +12,7 @@
|
|||
</div>
|
||||
|
||||
<div *ngIf="daemonRunning" class="tab-content" id="pills-tabContent">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-relay-tx" role="tabpanel" aria-labelledby="pills-relay-tx-tab" tabindex="0">
|
||||
<div class="row g-5 p-2">
|
||||
<div class="col-md-7 col-lg-12">
|
||||
|
@ -56,6 +57,52 @@
|
|||
</div>
|
||||
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" [disabled]="!validTxIds()" (click)="onRelay()">Relay Tx</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-send-raw-tx" role="tabpanel" aria-labelledby="pills-send-raw-tx-tab" tabindex="0">
|
||||
<div class="row g-5 p-2">
|
||||
<div class="col-md-7 col-lg-12">
|
||||
<div *ngIf="sendRawTxSuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-send-check m-2"></i></h4>
|
||||
<div>
|
||||
Txs sent successfully
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="sendRawTxError" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
|
||||
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>
|
||||
<div>
|
||||
{{sendRawTxError}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mb-3">Broadcast a raw transaction to the network</h4>
|
||||
<form class="needs-validation" novalidate="">
|
||||
<div class="row g-3">
|
||||
|
||||
<div class="col-12">
|
||||
<label for="send-raw-tx-tx-as-hex" class="form-label">Tx as hex</label>
|
||||
<textarea [(ngModel)]="rawTxJsonString" [ngModelOptions]="{standalone: true}" type="text" class="form-control" id="send-raw-tx-tx-as-hex" placeholder="de6a3..."
|
||||
rows="15" cols="15" ></textarea>
|
||||
<div class="invalid-feedback">
|
||||
Invalid transaction hex.
|
||||
</div>
|
||||
<small class="text-body-secondary">Full transaction information as hexadecimal string</small>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch col-md-6">
|
||||
<label for="send-raw-tx-do-not-relay" class="form-check-label">Do not relay</label>
|
||||
<input class="form-control form-check-input" type="checkbox" role="switch" id="send-raw-tx-do-not-relay" [checked]="sendRawTxDoNotRelay">
|
||||
<br>
|
||||
<small class="text-body-secondary">Stop relaying transaction to other nodes</small>
|
||||
</div>
|
||||
<hr class="my-4">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<button class="w-100 btn btn-primary btn-lg" type="button" (click)="sendRawTx()">Send Raw Tx</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-coinbase-tx-sum" role="tabpanel" aria-labelledby="pills-coinbase-tx-sum-tab" tabindex="0">
|
||||
<div class="row g-5 p-2">
|
||||
|
@ -160,7 +207,6 @@
|
|||
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" (click)="onFlushFromCache()">Flush Bad Txs</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
@ -31,9 +31,16 @@ export class TransactionsComponent implements AfterViewInit {
|
|||
|
||||
public daemonRunning: boolean = false;
|
||||
|
||||
public rawTxJsonString: string = '';
|
||||
public sendRawTxDoNotRelay: boolean = false;
|
||||
public sendRawTxSuccess: boolean = false;
|
||||
public sendRawTxError: string = '';
|
||||
public sendingRawTx: boolean = false;
|
||||
|
||||
constructor(private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
|
||||
this.navbarLinks = [
|
||||
new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', true, 'Relay Tx'),
|
||||
new NavbarLink('pills-send-raw-tx-tab', '#pills-send-raw-tx', 'pills-send-raw-tx', false, 'Send Raw Tx'),
|
||||
new NavbarLink('pills-tx-backlog-tab', '#pills-tx-backlog', 'pills-tx-backlog', false, 'Tx Backlog'),
|
||||
new NavbarLink('pills-coinbase-tx-sum-tab', '#pills-coinbase-tx-sum', 'pills-coinbase-tx-sum', false, 'Coinbase Tx Sum'),
|
||||
new NavbarLink('pills-flush-tx-pool-tab', '#pills-flush-tx-pool', 'pills-flush-tx-pool', false, 'Flush Tx Pool'),
|
||||
|
@ -152,4 +159,41 @@ export class TransactionsComponent implements AfterViewInit {
|
|||
this.getCoinbaseTxSumError = `${error}`;
|
||||
}
|
||||
}
|
||||
|
||||
public async sendRawTx(): Promise<void> {
|
||||
this.sendingRawTx = true;
|
||||
|
||||
try {
|
||||
const info = await this.daemonService.sendRawTransaction(this.rawTxJsonString, this.sendRawTxDoNotRelay);
|
||||
|
||||
if (info.doubleSpend) {
|
||||
throw new Error('Transaction is double spend');
|
||||
}
|
||||
else if (info.feeTooLow) {
|
||||
throw new Error('Fee is too low');
|
||||
}
|
||||
else if (info.invalidInput) {
|
||||
throw new Error('Input is invalid');
|
||||
}
|
||||
else if (info.invalidOutput) {
|
||||
throw new Error('Output is invalid');
|
||||
}
|
||||
else if (info.lowMixin) {
|
||||
throw new Error('Mixin count is too low');
|
||||
}
|
||||
else if (info.overspend) {
|
||||
throw new Error('Transaction uses more money than available')
|
||||
}
|
||||
else if (info.tooBig) {
|
||||
throw new Error('Transaction size is too big');
|
||||
}
|
||||
this.sendRawTxSuccess = true;
|
||||
this.sendRawTxError = '';
|
||||
}
|
||||
catch(error) {
|
||||
this.sendRawTxError = `${error}`;
|
||||
}
|
||||
|
||||
this.sendingRawTx = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ export class SidebarComponent implements OnChanges {
|
|||
new NavLink('Outputs', '/outputs', 'bi bi-circle-fill'),
|
||||
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'),
|
||||
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'),
|
||||
new NavLink('Network', '/network', 'bi bi-hdd-network'),
|
||||
new NavLink('XMRig', '/xmrig', 'icon-xr text-primary'),
|
||||
new NavLink('Bans', '/bans', 'bi bi-ban', 'bottom'),
|
||||
new NavLink('Logs', '/logs', 'bi bi-terminal', 'bottom'),
|
||||
|
|
20
src/common/request/SetLogCategoriesRequest.ts
Normal file
20
src/common/request/SetLogCategoriesRequest.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { RPCRequest } from "./RPCRequest";
|
||||
|
||||
export class SetLogCategoriesRequest extends RPCRequest {
|
||||
public override readonly method: 'set_log_categories' = 'set_log_categories';
|
||||
public override readonly restricted: true = true;
|
||||
|
||||
public readonly categories: string;
|
||||
|
||||
constructor(categories: string) {
|
||||
super();
|
||||
this.categories = categories;
|
||||
}
|
||||
|
||||
public toDictionary(): { [key: string]: any; } {
|
||||
return {
|
||||
'categories': this.categories
|
||||
}
|
||||
}
|
||||
|
||||
}
|
19
src/common/request/SetLogHashRateRequest.ts
Normal file
19
src/common/request/SetLogHashRateRequest.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { RPCRequest } from "./RPCRequest";
|
||||
|
||||
export class SetLogHashRateRequest extends RPCRequest {
|
||||
public override readonly method: 'set_log_hash_rate' = 'set_log_hash_rate';
|
||||
public override readonly restricted: boolean = true;
|
||||
|
||||
public readonly visible: boolean;
|
||||
|
||||
constructor(visible: boolean) {
|
||||
super();
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
return {
|
||||
'visible': this.visible
|
||||
};
|
||||
}
|
||||
}
|
23
src/common/request/SetLogLevelRequest.ts
Normal file
23
src/common/request/SetLogLevelRequest.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { RPCRequest } from "./RPCRequest";
|
||||
|
||||
export class SetLogLevelRequest extends RPCRequest {
|
||||
public override readonly method: 'set_log_level' = 'set_log_level';
|
||||
public override readonly restricted: boolean = true;
|
||||
|
||||
public readonly level: number;
|
||||
|
||||
constructor(level: number) {
|
||||
super();
|
||||
if (level < 0 || level > 4) {
|
||||
throw new Error(`Invalid log level provided: ${level}`);
|
||||
}
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public toDictionary(): { [key: string]: any; } {
|
||||
return {
|
||||
'level': this.level
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -54,6 +54,9 @@ export { IsKeyImageSpentRequest } from "./IsKeyImageSpentRequest";
|
|||
export { GetAltBlockHashesRequest } from "./GetAltBlockHashesRequest";
|
||||
export { SaveBcRequest } from "./SaveBcRequest";
|
||||
export { SetBootstrapDaemonRequest } from "./SetBootstrapDaemonRequest";
|
||||
export { SetLogLevelRequest } from "./SetLogLevelRequest";
|
||||
export { SetLogHashRateRequest } from "./SetLogHashRateRequest";
|
||||
export { SetLogCategoriesRequest } from "./SetLogCategoriesRequest";
|
||||
|
||||
/**
|
||||
* Restricted requests
|
||||
|
|
Loading…
Reference in a new issue