Fixed Firmware download from the Mikrotik website when there are multiple npk available
Fixed Mikrowizard system permission error when it is set to None
Fixed user device group permissions
Some minor UI improvements
Fix IP scan for one IP scan / Fix not scanning the last IP in the range
Fix manual snippet execution not working when device groups are selected
Some minor bug fixes and improvements

New:
Show background tasks and be able to stop them while running in the background (like an IP scanner)
Add support for manual MikroWizard update dashboard/settings page
update to version 1.0.5

Enhancement:
Show permission error in some pages when the  user doesn't have permission for that page/action
show better charts/graphs in the dashboard and device interface details
show more info on the dashboard about update and version information and license
This commit is contained in:
sepehr 2025-01-02 20:20:59 +03:00
parent 6dddfbcfab
commit 95d84faf17
19 changed files with 647 additions and 76 deletions

View file

@ -40,6 +40,7 @@ import {
SidebarModule,
TabsModule,
UtilitiesModule,
TableModule,
ModalModule
} from '@coreui/angular';
@ -85,7 +86,8 @@ export function loginStatusProviderFactory(provider: loginChecker) {
CardModule,
NgScrollbarModule,
ModalModule,
FontAwesomeModule
FontAwesomeModule,
TableModule
],
providers: [
{

View file

@ -49,6 +49,7 @@
</c-nav-item>
</c-header-nav> -->
<c-header-nav class="ms-3">
<ng-container *ngTemplateOutlet="taskDropdown"></ng-container> |
<ng-container *ngTemplateOutlet="userDropdown"></ng-container>
</c-header-nav>
@ -97,3 +98,45 @@
</ul>
</c-dropdown>
</ng-template>
<ng-template #taskDropdown>
<c-dropdown alignment="end" variant="nav-item">
<button
cButton
color=""
[caret]="false"
cDropdownToggle
class="py-0"
style="margin-right: 20px;"
>
<i class="fa-solid fa-gear"></i><c-badge color="danger" style="left: unset !important;" position="bottom-end" shape="rounded-pill">{{tasks.length}}</c-badge>
</button>
<ul cDropdownMenu class="pt-0 pr-5 w-auto">
<li>
<h6 cDropdownHeader class="bg-light fw-semibold py-2">Runnig Background Tasks</h6>
</li>
<li>
<table cTable small style="min-width: 250px;">
<thead style="font-size: 0.9rem;">
<tr>
<th scope="col"></th>
<th scope="col" style="color: darkgrey;">Task name</th>
<th scope="col" style="width: 60px;"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let task of tasks; let i = index">
<th scope="row">{{i+1}}</th>
<td>{{task.name}}</td>
<td>
<!-- <button (click)="stop_task(task['signal'])" cButton variant="ghost" color="danger" size="sm" ><i class="fa-solid fa-stop"></i></button> -->
<button (click)="callParentConfirm('CancelTask',task)" cButton variant="ghost" color="danger" size="sm" ><i class="fa-solid fa-stop"></i></button>
</td>
</tbody>
</table>
</li>
</ul>
</c-dropdown>
</ng-template>

View file

@ -15,6 +15,7 @@ export class DefaultHeaderComponent extends HeaderComponent {
@Input() sidebarId: string = "sidebar";
@Output() UserModalEvent = new EventEmitter<any>();
@Output() ConfirmModalEvent = new EventEmitter<any>();
public newMessages = new Array(4)
public newTasks = new Array(5)
@ -25,7 +26,9 @@ export class DefaultHeaderComponent extends HeaderComponent {
public uname: string;
public fname: string;
public lname: string;
public UserProfileModalVisible : boolean = false;
public ConfirmModalVisible : boolean = false;
public tasks : any = [];
public timer : any;
constructor(
private classToggler: ClassToggleService,
@ -53,7 +56,9 @@ export class DefaultHeaderComponent extends HeaderComponent {
callParent(action:string): void {
this.UserModalEvent.next(action);
}
callParentConfirm(action:string,data:any): void {
this.ConfirmModalEvent.next({action:action,data:data});
}
logout() {
this.data_provider.logout().then(res => {
this.router.navigate(['login']);
@ -62,6 +67,27 @@ export class DefaultHeaderComponent extends HeaderComponent {
ngOnInit(): void {
var _self = this;
console.log('DefaultHeaderComponent');
this.get_user_info();
this.data_provider.get_running_tasks().then(res => {
_self.tasks = res['tasks'].filter((x:any) => x.status);
})
// get running tasks every 5 seconds
this.timer=setInterval(function(){
_self.get_running_tasks();
}, 5000);
}
get_running_tasks(){
var _self = this;
this.data_provider.get_running_tasks().then(res => {
_self.tasks = res['tasks'].filter((x:any) => x.status);
})
}
ngOnDestroy(): void {
clearInterval(this.timer);
}
}

View file

@ -21,7 +21,7 @@
<!--main-->
<div class="wrapper d-flex flex-column min-vh-100 bg-light dark:bg-transparent">
<!--app-header-->
<app-default-header (UserModalEvent)="show_user_modal($event)" class="mb-2 d-print-none header header-sticky"
<app-default-header (UserModalEvent)="show_user_modal($event)" (ConfirmModalEvent)="show_confirm_modal($event)" class="mb-2 d-print-none header header-sticky"
position="sticky" sidebarId="sidebar" />
<!--app-body-->
<div class="main-container body flex-grow-1 px-3" style="display: flex;">
@ -105,4 +105,34 @@
Close
</button>
</c-modal-footer>
</c-modal>
<c-modal #ConfirmModal backdrop="static" size="lg" [(visible)]="ConfirmModalVisible" id="ConfirmModal">
<c-modal-header>
<h5 cModalTitle *ngIf="action=='CancelTask'">Please Confirm Stopping Background Task</h5>
<h5 cModalTitle *ngIf="action=='update'">Please Confirm Stopping Background Task</h5>
<button [cModalToggle]="ConfirmModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body >
<div *ngIf="action=='CancelTask'">
<p>Are you sure you want to stop the task <code style="padding: 0 !important;"><b>{{ data['name'] }}</b></code>?</p>
</div>
<div *ngIf="action=='CancelTask' && data['signal']!='130' && data['signal']!='140'">
<code>Stopping this task will cause reload of other background tasks</code>
</div>
<div *ngIf="action=='update'">
<code >Clear browser cache and Reload the page or hit Ctrl+F5 to load latest Mikrofront version</code>
</div>
</c-modal-body>
<c-modal-footer>
<button *ngIf="action!='update'" cButton (click)="ConfirmAction()" color="primary" [disabled]="data['SubmitDisable']"><i *ngIf="data['SubmitDisable']" class="fa-solid fa-spinner fa-spin"></i> submit</button>
<button *ngIf="action!='update'" [cModalToggle]="ConfirmModal.id" cButton color="secondary">
</button>
<button *ngIf="action=='update'" cButton (click)="ConfirmAction()" color="primary" ><i *ngIf="data['SubmitDisable']" class="fa-solid fa-spinner fa-spin"></i> Reload</button>
<button *ngIf="action=='update'" (click)="clearTimer()" cButton color="secondary">
Close
</button>
</c-modal-footer>
</c-modal>

View file

@ -6,6 +6,7 @@ import { navItems } from './_nav';
import { dataProvider } from '../../providers/mikrowizard/data';
import { arch } from 'os';
import { DomSanitizer } from '@angular/platform-browser';
import { disconnect } from 'process';
@Component({
selector: 'app-dashboard',
@ -23,11 +24,14 @@ export class DefaultLayoutComponent implements OnInit {
public ispro: boolean=false;
public action: string="password";
public UserProfileModalVisible:boolean;
public ConfirmModalVisible:boolean;
public error:any=false;
public currentStep:number=1;
public qrCode:any=false;
public totpCode:string='';
public errorMessage:any=false;
public data:any={};
public timer:any;
public password:any={
'cupass':'',
'pass1':'',
@ -130,7 +134,41 @@ export class DefaultLayoutComponent implements OnInit {
else
this.UserProfileModalVisible = true;
}
show_confirm_modal(data:any){
this.data={};
if (data.action=='CancelTask'){
this.action=data.action;
this.data=data.data;
console.dir(this.data);
console.dir(this.action);
//disable submit button
this.data['SubmitDisable']=false;
this.ConfirmModalVisible = true;
}
if (data.action=='update'){
this.action='update';
this.data={};
this.ConfirmModalVisible = true;
}
}
ConfirmAction(){
var _self=this;
if(this.action=='CancelTask'){
this.data_provider.stop_task(this.data['signal']).then(res => {
//disable submit button
if(res['status']=='success'){
setTimeout(function () {
_self.ConfirmModalVisible = false;
}, 5000);
}
this.data['SubmitDisable']=true;
//wait 5 seconds before hiding the modal
})
}
if(this.action=='update'){
window.location.href = window.location.href.replace(/#.*$/, '')
}
}
submit(){
var _self=this;
if(!_self.passvalid['pass2']){
@ -190,12 +228,28 @@ export class DefaultLayoutComponent implements OnInit {
}
});
});
_self.data_provider.get_front_version().then((res:any) => {
if(res['version']!=this.version){
console.dir("New version is available. Please refresh the page.");
window.location.href = window.location.href.replace(/#.*$/, '');
}
});
// check first time after 10 seconds
setTimeout(function(){
_self.data_provider.get_front_version().then((res:any) => {
if(res['version']!=_self.version){
console.log("New version is available. Please refresh the page.");
_self.show_confirm_modal({action:'update'});
// window.location.href = window.location.href.replace(/#.*$/, '');
}
});
}, 10000);
// check for new version every 5 seconds
this.timer=setInterval(function(){
_self.data_provider.get_front_version().then((res:any) => {
if(res['version']!=_self.version){
console.log("New version is available. Please refresh the page.");
_self.show_confirm_modal({action:'update'});
// window.location.href = window.location.href.replace(/#.*$/, '');
}
});
}, 60000);
}
clearTimer() { clearInterval(this.timer); }
}

View file

@ -70,9 +70,10 @@ export class dataProvider {
}
return this.MikroWizardRPC.sendJsonRequest("/api/user/change_password", data);
}
dashboard_stats(versioncheck:boolean){
dashboard_stats(versioncheck:boolean,front_version:string){
var data={
'versioncheck':versioncheck
'versioncheck':versioncheck,
'front_version':front_version
}
return this.MikroWizardRPC.sendJsonRequest("/api/dashboard/stats", data);
}
@ -537,7 +538,21 @@ export class dataProvider {
return this.MikroWizardRPC.sendJsonRequest("/api/sysconfig/save_all", data);
}
get_running_tasks(){
return this.MikroWizardRPC.sendJsonRequest("/api/tasks/list", {});
}
stop_task(signal:number){
var data={
'signal':signal
}
return this.MikroWizardRPC.sendJsonRequest("/api/tasks/stop", data);
}
apply_update(action:string){
var data={
'action':action
}
return this.MikroWizardRPC.sendJsonRequest("/api/sysconfig/apply_update", data);
}
////
//// End api funcs
////

View file

@ -125,29 +125,72 @@
</c-card-body>
</c-card>
<c-row>
<c-col xl="6" *ngIf="stats" lg="12" class="h-100" style="height: 160px!important;">
<c-widget-stat-b [title]="stats['version']" class="mb-1 h-100" value="Version">
<c-col xl="6" *ngIf="stats" lg="12" class="h-100" style="min-height: 160px!important;display: grid">
<c-card class="mb-1 p-1 h-100" style="padding-left: 5px!important;">
<div class="my-1">
<code style="padding: 0!important;">Serial:</code> <small
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success mx-1"><i
class="fa-solid fa-check"></i>Copy</span>
<h4 style="display: inline-block;">Version and Serial information</h4>
</div>
<div *ngIf="!stats['license']" class="my-1">
<c-badge color="danger">Not Registred OR Not internet access</c-badge>
<div style="display: inline-block;margin-right: 5px;">
<code style="padding: 0!important;">Serial:</code> <small
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success"><i
class="fa-solid fa-check"></i>Copy</span>
</div>
<c-badge color="danger">Not Registred</c-badge>
<a class="mx-1" target="_blank" href="http://MikroWizard.com">Learn how to register and get automatic
updates!</a>
</div>
<div *ngIf="stats['license']" class="my-1">
<div *ngIf="stats['license']=='connection_error'" class="my-1">
<c-badge class="mx-1" color="danger">Unable connect to server/Check server internet connection</c-badge>
</div>
<div *ngIf="stats['license']!='connection_error'" class="my-1">
<div style="display: inline-block;margin-right: 5px;">
<code style="padding: 0!important;">Serial:</code> <small
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success mx-1"><i
class="fa-solid fa-check"></i>Copy</span>
</div>
<c-badge color="success">Registred</c-badge>
<c-badge class="mx-1" color="info">License Type : {{stats['license']}}</c-badge>
<c-badge *ngIf="stats['update_mode']!='auto'" color="info">Manual update</c-badge>
<c-badge *ngIf="stats['update_mode']=='auto'" color="info">Auto update</c-badge>
</div>
</c-widget-stat-b>
<div *ngIf="stats['license']!='connection_error'" class="my-1">
<span style="font-size: 0.9rem; display: inline-block;margin-right: 5px"><c-badge
[color]="stats['update_available'] ? 'success' : 'secondary'"
style="margin: 0!important;padding: 8px;height: 27px;">Your Mikroman version : {{stats['version']}}
</c-badge>
<i class="fa-solid fa-spinner fa-spin" *ngIf="stats['update_inprogress']"></i>
<button cButton color="warning"
*ngIf="stats['update_mode']!='auto' && stats['update_available'] && !stats['update_inprogress']" size="sm"
(click)="showConfirmModal('update_mikroman')"
style="font-size: 0.75em;position: relative;left: -4px;top: 1px;border-top-left-radius: 0;border-bottom-left-radius: 0;height: 27px;"><i
class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update availble </button>
</span>
<span style="font-size: 0.9rem; display: inline-block;"><c-badge
[color]="stats['front_update_available'] ? 'success' : 'secondary'" style="padding: 8px;height: 27px;"
color="secondary">Your Mikrofront version : {{front_version}}
</c-badge>
<i class="fa-solid fa-spinner fa-spin" *ngIf="stats['front_update_inprogress']"></i>
<button cButton color="warning"
*ngIf="stats['update_mode']!='auto' && stats['front_update_available'] && !stats['front_update_inprogress']"
size="sm" (click)="showConfirmModal('update_mikrofront')"
style="font-size: 0.75em;position: relative;left: -4px;top: 1px;border-top-left-radius: 0;border-bottom-left-radius: 0;height: 27px;"><i
class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update availble </button>
</span>
</div>
<!-- <div *ngIf="stats['update_mode']!='auto'" class="my-1">
<button cButton color="warning" *ngIf="stats['update_available']" size="sm" style="font-size: 1em;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>Mikroman</strong> and reload server</button>
<button cButton color="warning" *ngIf="stats['front_update_available']" size="sm" style="font-size: 1em;margin-left: 5px;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>MikroFront</strong> and reload Page</button>
</div> -->
</c-card>
</c-col>
<c-col xl="6" lg="12" class="h-100" style="height: 160px!important;">
<c-card class="mb-1 p-1 h-100" *ngIf="stats">
<c-col xl="6" lg="12" class="h-100" style="min-height: 160px!important;display: grid;">
<c-card class="h-100" *ngIf="stats" style="padding: 0!important;margin: 0!important;">
<c-carousel [dark]="true" [animate]="false" [wrap]="false" [interval]="1000000">
<c-carousel-indicators></c-carousel-indicators>
<c-carousel-inner>
@ -165,4 +208,38 @@
</c-carousel>
</c-card>
</c-col>
</c-row>
</c-row>
<c-modal #ConfirmModal backdrop="static" size="lg" [(visible)]="ConfirmModalVisible" id="ConfirmModal">
<c-modal-header>
<h5 cModalTitle *ngIf="action=='update_mikroman'">Please Confirm Mikroman Update</h5>
<h5 cModalTitle *ngIf="action=='update_mikrofront'">Please Confirm MikroFront Update</h5>
<button [cModalToggle]="ConfirmModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<div *ngIf="action=='update_mikroman'">
<p>Are you sure you want to apply latest Mikroman Update <code
style="padding: 0 !important;"><b> ver {{ stats['latest_version'] }}</b></code>?</p>
<p>By updating Mikroman the MikroFront update is also get checked and applyed</p>
<p>If you made any special changes to configuration files or python files it will be removed automaticlaly</p>
</div>
<div *ngIf="action=='update_mikroman'">
<code style="padding: 0!important;">Applying the update will cause reload of the server couple of times</code>
</div>
<div *ngIf="action=='update_mikrofront'">
<p>Are you sure you want to apply latest MikroFront Update <code
style="padding: 0 !important;"><b> ver {{ stats['front_latest_version'] }}</b></code>?</p>
</div>
<div *ngIf="action=='update_mikrofront'">
<code style="padding: 0!important;">Applying the update will cause reload of the page,<br>
Also please make sure you have the latest Mikroman before updating MikroFront.
Updating to latest MikroFront without getting latest Mikroman can cause problems</code>
</div>
</c-modal-body>
<c-modal-footer>
<button cButton (click)="ConfirmAction()" color="primary"> submit</button>
<button [cModalToggle]="ConfirmModal.id" cButton color="secondary">
Close
</button>
</c-modal-footer>
</c-modal>

View file

@ -14,6 +14,9 @@ export class DashboardComponent implements OnInit {
public uname: string;
public tz: string;
public copy_msg: any = false;
public ConfirmModalVisible: boolean = false;
public action: string = "";
front_version=require('../../../../package.json').version;
constructor(
private data_provider: dataProvider,
private router: Router,
@ -41,7 +44,9 @@ export class DashboardComponent implements OnInit {
trafficRadio: new UntypedFormControl("5m"),
});
public chart_data: any = {};
Chartoptions = {
public Chartoptions = {
responsive: true,
plugins: {
tooltip: {
callbacks: {
@ -87,17 +92,17 @@ export class DashboardComponent implements OnInit {
stacked: true,
position: "left",
type: "linear",
color: "#17522f",
color: "#4caf50",
grid: {
color: "rgba(23, 82, 47, 0.3)",
color: "rgba(76, 175, 79, 0.3)",
backgroundColor: "transparent",
borderColor: "#f86c6b",
pointHoverBackgroundColor: "#f86c6b",
borderColor: "#4caf50",
pointHoverBackgroundColor: "#4caf50",
borderWidth: 1,
borderDash: [8, 5],
},
ticks: {
color: "#17522f",
color: "#000000",
callback: function (value: any, index: any, ticks: any) {
const units = ["bit", "Kib", "Mib", "Gib", "Tib"];
var res = value;
@ -119,10 +124,10 @@ export class DashboardComponent implements OnInit {
position: "right",
type: "linear",
grid: {
color: "rgba(23, 82, 47, 0.3)",
color: "rgba(255, 152, 0, 0.4)",
backgroundColor: "transparent",
borderColor: "#f86c6b",
pointHoverBackgroundColor: "#f86c6b",
borderColor: "#ff9800",
pointHoverBackgroundColor: "#ff9800",
borderWidth: 1,
borderDash: [8, 5],
},
@ -130,7 +135,7 @@ export class DashboardComponent implements OnInit {
width: 2,
},
ticks: {
color: "#171951",
color: "#000000",
callback: function (value: any, index: any, ticks: any) {
const units = ["bit", "Kib", "Mib", "Gib", "Tib"];
var res = value;
@ -147,7 +152,7 @@ export class DashboardComponent implements OnInit {
elements: {
line: {
borderWidth: 1,
tension: 0.4,
tension: 0.1,
},
point: {
radius: 2,
@ -181,7 +186,7 @@ export class DashboardComponent implements OnInit {
}
initStats() {
var _self = this;
this.data_provider.dashboard_stats(true).then((res) => {
this.data_provider.dashboard_stats(true,this.front_version).then((res) => {
_self.stats = res;
});
}
@ -200,4 +205,23 @@ export class DashboardComponent implements OnInit {
this.delta = value;
this.initTrafficChart();
}
showConfirmModal(action: string) {
this.action = action;
this.ConfirmModalVisible = true
}
ConfirmAction() {
var _self = this;
this.data_provider.apply_update(this.action).then((res) => {
if (res["status"]=='success') {
if (_self.action=='update_mikroman') {
_self.stats['update_inprogress']=true;
}
if (_self.action=='update_mikrofront') {
_self.stats['front_update_inprogress']=true;
}
_self.action="";
_self.ConfirmModalVisible = false;
}
});
}
}

View file

@ -11,6 +11,7 @@ import {
ProgressModule,
TemplateIdDirective,
BadgeModule,
ModalModule,
CarouselModule,
} from "@coreui/angular";
@ -37,6 +38,7 @@ import { ClipboardModule } from "@angular/cdk/clipboard";
CarouselModule,
BadgeModule,
ClipboardModule,
ModalModule,
],
declarations: [DashboardComponent],
})

View file

@ -123,8 +123,9 @@ export class DeviceComponent implements OnInit, OnDestroy {
type: GuiRowSelectionType.CHECKBOX,
mode: GuiRowSelectionMode.MULTIPLE,
};
Chartoptions = {
responsive: true,
_self :this,
plugins: {
tooltip: {
callbacks: {
@ -164,7 +165,19 @@ export class DeviceComponent implements OnInit, OnDestroy {
},
maintainAspectRatio: true,
scales: {
x: { display: false },
x: {
title: {
display: true,
text: 'Time',
color: '#333',
},
ticks: {
autoSkip: true,
maxTicksLimit: 30,
color: '#333',
}
},
yA: {
display: true,
stacked: true,
@ -177,7 +190,9 @@ export class DeviceComponent implements OnInit, OnDestroy {
},
ticks: {
color: "#17522f",
callback: function (value: any, index: any, ticks: any) {
callback: (value: any) => {
if(this.total_type=="pps")
return value + " pps";
const units = ["bit", "Kib", "Mib", "Gib", "Tib"];
var res = value;
let unitIndex = 0;
@ -206,7 +221,9 @@ export class DeviceComponent implements OnInit, OnDestroy {
},
ticks: {
color: "#171951",
callback: function (value: any, index: any, ticks: any) {
callback: (value: any) =>{
if(this.total_type=="pps")
return value + " pps";
const units = ["bit", "Kib", "Mib", "Gib", "Tib"];
var res = value;
let unitIndex = 0;
@ -222,7 +239,7 @@ export class DeviceComponent implements OnInit, OnDestroy {
elements: {
line: {
borderWidth: 1,
tension: 0.4,
tension: 0.1,
},
point: {
radius: 4,

View file

@ -265,12 +265,11 @@
<c-modal #ExecutedDataModal backdrop="static" size="xl" [(visible)]="ExecutedDataModalVisible" id="ExecutedDataModal">
<c-modal-header>
<h5 cModalTitle>Editing Group </h5>
<h5 cModalTitle>Scan History : </h5>
<button (click)="ExecutedDataModalVisible=!ExecutedDataModalVisible" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<c-input-group class="mb-3">
<h5>Group Members :</h5>
<gui-grid [autoResizeWidth]="true" *ngIf="ExecutedDataModalVisible" [searching]="searching"
[source]="ExecutedData" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
[autoResizeWidth]=true
@ -283,8 +282,8 @@
<ng-template let-value="item['start_ip']" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
</gui-grid-column>
<gui-grid-column header="End ip" field="start_ip">
<ng-template let-value="item['start_ip']" let-item="item" let-index="index">
<gui-grid-column header="End ip" field="end_ip">
<ng-template let-value="item['end_ip']" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
</gui-grid-column>
<gui-grid-column header="End time" field="end">

View file

@ -194,7 +194,7 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.selected_device = dev;
this.data_provider.get_editform(dev.id).then((res) => {
if ("error" in res) {
if (res.error.indexOf("Unauthorized")) {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
@ -230,8 +230,17 @@ export class DevicesComponent implements OnInit, OnDestroy {
var _self = this;
this.ConfirmModalVisible = false;
this.data_provider.delete_devices(this.Selectedrows).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("Success", "Device Deleted", "success");
this.initGridTable();
}
});
}
@ -314,6 +323,14 @@ export class DevicesComponent implements OnInit, OnDestroy {
});
}
}
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else
_self.scanwizard_step = step;
});
}
@ -357,11 +374,20 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.data_provider
.check_firmware(this.Selectedrows.toString())
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("info", "Checking Firmwares", "light");
_self.ConfirmModalVisible = false;
setTimeout(function () {
if (_self.Selectedrows.length < 1) _self.initGridTable();
}, 1);
}
});
}
@ -370,8 +396,16 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.data_provider
.update_firmware(this.Selectedrows.toString())
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("info", "Updating Firmwares Sent", "light");
_self.initGridTable();
_self.initGridTable();}
});
}
@ -380,8 +414,17 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.data_provider
.upgrade_firmware(this.Selectedrows.toString())
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("info", "Upgrading Firmwares", "light");
_self.initGridTable();
}
});
}
@ -390,19 +433,37 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.data_provider
.reboot_devices(this.Selectedrows.toString())
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("info", "Reboot sent", "light");
_self.ConfirmModalVisible = !_self.ConfirmModalVisible;
_self.initGridTable();
}
});
}
get_groups() {
var _self = this;
this.data_provider.get_devgroup_list().then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
if( "status" in res && res.status == 'failed' )
_self.groups = false
else
_self.groups = res;
}
});
}
@ -417,6 +478,14 @@ export class DevicesComponent implements OnInit, OnDestroy {
};
_self.data_provider.get_dev_list(data).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.source = res.map((x: any) => {
if (x.upgrade_availble) _self.upgrades.push(x);
if (x.update_availble) _self.updates.push(x);
@ -424,6 +493,7 @@ export class DevicesComponent implements OnInit, OnDestroy {
});
_self.device_interval();
_self.loading = false;
}
});
}
@ -516,8 +586,17 @@ export class DevicesComponent implements OnInit, OnDestroy {
_self.selected_device['editform']['password']="Loading ...";
if (_self.ispro && !this.show_pass){
_self.data_provider.get_device_pass(this.selected_device['id']).then((res) => {
if ("error" in res && "error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.selected_device['editform']['password']=res['password'];
this.show_pass=!this.show_pass;
}
});
}
else{
@ -530,7 +609,14 @@ export class DevicesComponent implements OnInit, OnDestroy {
this.data_provider
.scan_results()
.then((res) => {
console.dir(res);
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
let index = 1;
_self.ExecutedData= res.data.map((d: any) => {
d.index = index;
@ -551,6 +637,7 @@ export class DevicesComponent implements OnInit, OnDestroy {
index += 1;
return d;
});
}
});
}

View file

@ -225,17 +225,26 @@ export class PermissionsComponent implements OnInit {
} else {
var _self = this;
this.data_provider.delete_perm(_self.SelectedPerm["id"]).then((res) => {
if (res["status"] == "failed") {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
res.err,
"You are not authorized to perform this action",
"danger"
);
return;
}
else{
_self.initGridTable();
_self.DeleteConfirmModalVisible = false;
if (res["status"] == "failed") {
_self.show_toast(
"Error",
res.err,
"danger"
);
return;
}
else{
_self.initGridTable();
_self.DeleteConfirmModalVisible = false;
}
}
});
}

View file

@ -72,7 +72,7 @@
<option value="keep">Keep v6 and don't update to v7</option>
<option value="update">install latest</option>
</select>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Choose how Mikrowizard should update old v6 firmwares</c-form-feedback>
</c-input-group>
@ -85,7 +85,7 @@
<option *ngFor="let f of available_firmwares" [value]="f">{{f}}</option>
</select>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* The version of firmware to install routers</c-form-feedback>
</c-input-group>
@ -97,7 +97,7 @@
<option>Choose...</option>
<option *ngFor="let f of available_firmwaresv6" [value]="f">{{f}}</option>
</select>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* The version of firmware to install on V6 routers</c-form-feedback>
</c-input-group>
<button cButton color="primary" (click)="saveFirmwareSetting()">Save</button>
@ -112,7 +112,7 @@
<span cInputGroupText>Rad Secret</span>
<input cFormControl id="floatingInput" placeholder="rad_secret"
[(ngModel)]="sysconfigs['rad_secret']['value']" />
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Radius Secret of Mikrowizard Radius Server</c-form-feedback>
</c-input-group>
@ -121,7 +121,7 @@
<span cInputGroupText>System URL</span>
<input cFormControl id="floatingInput" placeholder="System URL"
[(ngModel)]="sysconfigs['system_url']['value']" />
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Default system access URl</c-form-feedback>
</c-input-group>
@ -129,7 +129,7 @@
<span cInputGroupText>Default IP</span>
<input cFormControl id="floatingInput" placeholder="System URL"
[(ngModel)]="sysconfigs['default_ip']['value']" />
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Default Mikrowizard Access IP</c-form-feedback>
</c-input-group>
<c-input-group class="mt-3">
@ -146,7 +146,7 @@
</mat-option>
</mat-select>
</mat-form-field>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Default TimeZone for the system</c-form-feedback>
</c-input-group>
@ -157,20 +157,34 @@
<span cInputGroupText>Default password</span>
<input aria-label="Password" type="password" [(ngModel)]="sysconfigs['default_password']['value']"
cFormControl />
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Default username and Password for searching new devices</c-form-feedback>
</c-input-group>
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Mikrowizard Update Mode
</label>
<select cSelect [(ngModel)]="sysconfigs['update_mode']['value']['mode']" id="inputGroupSelect01">
<option>Choose...</option>
<option value="auto">Automatic Update</option>
<option value="manual">Show update only/Update manually</option>
</select>
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Choose if Mikrowizard should download updates automaticaly when availble or wait for user to download/apply updates</c-form-feedback>
</c-input-group>
<c-input-group class="mt-3 mb-3">
<span cInputGroupText>License Username</span>
<input aria-label="Username" type="text" [(ngModel)]="sysconfigs['username']['value']" cFormControl />
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* The username that you registred in Mikrowizard.com,Required for License Activation</c-form-feedback>
</c-input-group>
<c-form-check [switch]="true" sizing="xl">
<input cFormCheckInput [(ngModel)]="sysconfigs['force_perms']['value']" type="checkbox" />
<label cFormCheckLabel>Force Perms</label>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Force User Groups under user>groups configuration of each router to match Mikrowizard Permissions and
monitor for any change to prevent/fix the configuration.</c-form-feedback>
</c-form-check>
@ -178,7 +192,7 @@
<c-form-check [switch]="true" sizing="xl">
<input cFormCheckInput [(ngModel)]="sysconfigs['force_radius']['value']" type="checkbox" />
<label cFormCheckLabel>Force Radius</label>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Force Radius config under radius>client and user>aaa setting of each router that added to Mikrowizard and
monitor for any change to prevent/fix the configuration.</c-form-feedback>
</c-form-check>
@ -186,7 +200,7 @@
<c-form-check [switch]="true" sizing="xl">
<input cFormCheckInput [(ngModel)]="sysconfigs['force_syslog']['value']" type="checkbox" />
<label cFormCheckLabel>Force Syslog</label>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true">
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true">
* Force Syslog config under system>logs setting of each router that added to Mikrowizard and monitor syslog
setting for any change to prevent/fix the configuration.</c-form-feedback>
</c-form-check>
@ -194,7 +208,7 @@
<c-form-check *ngIf="ispro" [switch]="true" sizing="xl">
<input cFormCheckInput [(ngModel)]="sysconfigs['safe_install']['value']" type="checkbox" />
<label cFormCheckLabel>Safe Update</label>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true"><code style="padding: 0!important;">PRO</code>
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true"><code style="padding: 0!important;">PRO</code>
* Download and install reqired firmware before installing the target firmware . for example it will install
latest 7.12 then upgrade to newer version >7.13 or install Required packages before update</c-form-feedback>
</c-form-check>
@ -202,7 +216,7 @@
<c-form-check *ngIf="ispro" [switch]="true" sizing="xl">
<input cFormCheckInput [(ngModel)]="sysconfigs['otp_force']['value']" type="checkbox" />
<label cFormCheckLabel>Force device otp</label>
<c-form-feedback style="display: block;color: #979797;margin-top: 0;" [valid]="true"><code style="padding: 0!important;">PRO</code>
<c-form-feedback style="display: block;color: #5c5c5c;margin-top: 0;" [valid]="true"><code style="padding: 0!important;">PRO</code>
* Force login to devices using otp for all users.(you can make exceptions for each user)</c-form-feedback>
</c-form-check>
<button cButton color="primary" (click)="saveSysSetting()">Save</button>

View file

@ -7,4 +7,7 @@
}
.mdc-line-ripple.mdc-line-ripple--deactivating.ng-star-inserted {
display: none!important;
}
.form-check-label{
font-weight: bold;
}

View file

@ -22,7 +22,6 @@ import {
import { ToasterComponent } from "@coreui/angular";
import { AppToastComponent } from "../toast-simple/toast.component";
import { TimeZones } from "./timezones-data";
import { error } from "console";
@Component({
templateUrl: "settings.component.html",
@ -137,6 +136,14 @@ export class SettingsComponent implements OnInit {
_self.currentFirm=firm;
if(del){
this.data_provider.delete_firm(this.currentFirm.id).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
if (res.status == true){
_self.DeleteConfirmModalVisible=false;
_self.initFirmsTable();
@ -148,6 +155,7 @@ export class SettingsComponent implements OnInit {
"danger"
);
}
}
});
}
else
@ -160,6 +168,14 @@ export class SettingsComponent implements OnInit {
this.data_provider
.download_firmware_to_repository(this.firmtodownload)
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
if (res.status == true) {
// show toast that we are already downloading
_self.show_toast(
@ -177,6 +193,7 @@ export class SettingsComponent implements OnInit {
}
_self.ConfirmModalVisible = !_self.ConfirmModalVisible;
_self.loading = false;
}
});
}
@ -204,14 +221,34 @@ export class SettingsComponent implements OnInit {
this.firmwaretoinstallv6
)
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.initFirmsTable();
}
});
}
saveSysSetting() {
var _self = this;
this.data_provider.save_sys_setting(this.sysconfigs).then((res) => {
_self.initsettings();
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.show_toast("Settings", "Settings saved", "success");
_self.initsettings();
}
});
}
@ -244,6 +281,14 @@ export class SettingsComponent implements OnInit {
initsettings(): void {
var _self = this;
this.data_provider.get_settings().then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.sysconfigs = res.sysconfigs;
_self.sysconfigs["default_user"]["value"] = "";
_self.sysconfigs["default_password"]["value"] = "";
@ -265,7 +310,23 @@ export class SettingsComponent implements OnInit {
_self.sysconfigs["otp_force"]["value"]
);
}
//check if update_mode is in the sysconfigs
if ("update_mode" in _self.sysconfigs){
//convert string to json
_self.sysconfigs["update_mode"]["value"] = JSON.parse(_self.sysconfigs["update_mode"]["value"]);
}
else{
//create default update_mode and set mode to auto
_self.sysconfigs["update_mode"] = {
"value": {
"mode": "auto",
"update_back" : false,
"update_front" : false
}
}
}
_self.SysConfigloading = false;
}
});
}

View file

@ -159,12 +159,11 @@
<c-modal #ExecutedDataModal backdrop="static" size="lg" [(visible)]="ExecutedDataModalVisible" id="ExecutedDataModal">
<c-modal-header>
<h5 cModalTitle>Editing Group </h5>
<h5 cModalTitle>Exec history </h5>
<button (click)="ExecutedDataModalVisible=!ExecutedDataModalVisible" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<c-input-group class="mb-3">
<h5>Group Members :</h5>
<gui-grid [autoResizeWidth]="true" *ngIf="ExecutedDataModalVisible" [searching]="searching"
[source]="ExecutedData" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
[autoResizeWidth]=true

View file

@ -171,6 +171,14 @@ export class UserManagerComponent implements OnInit {
}
_self.SelectedUser["adminperms"] = _self.adminperms;
this.data_provider.create_user(_self.SelectedUser).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
if ("id" in res && !("status" in res)) {
_self.initGridTable();
this.EditTaskModalVisible = false;
@ -178,6 +186,7 @@ export class UserManagerComponent implements OnInit {
//show error
_self.show_toast("Error", res.err, "danger");
}
}
});
} else {
if (_self.userperms.length > 0) {
@ -187,8 +196,17 @@ export class UserManagerComponent implements OnInit {
}
_self.SelectedUser["adminperms"] = _self.adminperms;
this.data_provider.edit_user(_self.SelectedUser).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.initGridTable();
_self.EditTaskModalVisible = false;
}
});
}
//
@ -197,14 +215,32 @@ export class UserManagerComponent implements OnInit {
editAddUser(item: any, action: string) {
var _self = this;
this.data_provider.get_perms(1, 1000, "").then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.allPerms = res.map((x: any) => {
return { id: x["id"], name: x.name };
});
_self.data_provider.get_devgroup_list().then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.allDevGroups = res.map((x: any) => {
return { id: x["id"], name: x.name };
});
}
});
}
});
if (action == "showadd") {
this.userperms = [];
@ -241,7 +277,6 @@ export class UserManagerComponent implements OnInit {
this.data_provider.get_user_restrictions(this.SelectedUser["id"]).then((res) => {
_self.userresttrictions = res;
console.log(_self.userresttrictions);
_self.RestrictionsTaskModalVisible = true;
});
}
@ -270,13 +305,23 @@ export class UserManagerComponent implements OnInit {
}
save_sec(){
var _self=this;
this.data_provider.save_user_restrictions(this.SelectedUser.id,this.userresttrictions).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
if('status' in res && res['status']=='success')
this.RestrictionsTaskModalVisible = false;
else if('status' in res && res['status']=='failed')
this.show_toast("Error", res.err, "danger");
else
this.show_toast("Error", "Somthing went wrong", "danger");
}
});
}
@ -289,9 +334,18 @@ export class UserManagerComponent implements OnInit {
this.devgroup["id"]
)
.then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.get_user_perms(_self.SelectedUser["id"]);
_self.permission = 0;
_self.devgroup = 0;
}
});
}
@ -314,8 +368,17 @@ export class UserManagerComponent implements OnInit {
} else {
var _self = this;
this.data_provider.delete_user(_self.SelectedUser["id"]).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.initGridTable();
_self.DeleteConfirmModalVisible = false;
}
});
}
}
@ -329,8 +392,18 @@ export class UserManagerComponent implements OnInit {
}
confirm_delete_perm(item: any) {
var _self = this;
this.data_provider.Delete_user_perm(item.id).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
this.get_user_perms(this.SelectedUser["id"]);
}
});
}
@ -344,11 +417,20 @@ export class UserManagerComponent implements OnInit {
var pageSize = 10;
var searchstr = "";
this.data_provider.get_users(page, pageSize, searchstr).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.source = res.map((x: any) => {
return x;
});
_self.SelectedUser = {};
_self.loading = false;
}
});
}
}

View file

@ -201,6 +201,14 @@ export class VaultComponent implements OnInit {
get_passwords(){
var _self=this;
this.data_provider.get_passwords(this.filters).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.passwords=res.data.map((d: any) => {
d.changed = formatInTimeZone(
d.changed.split(".")[0] + ".000Z",
@ -209,6 +217,7 @@ export class VaultComponent implements OnInit {
);
return d;
});
}
});
}
@ -216,8 +225,17 @@ export class VaultComponent implements OnInit {
var _self=this;
_self.password="";
this.data_provider.reveal_password(devid,username).then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
_self.password=res.password;
_self.PasswordModalVisible=true;
}
});
}
@ -255,6 +273,14 @@ export class VaultComponent implements OnInit {
get_vault_history(){
var _self=this;
this.data_provider.vault_history().then((res) => {
if ("error" in res && res.error.indexOf("Unauthorized")) {
_self.show_toast(
"Error",
"You are not authorized to perform this action",
"danger"
);
}
else{
let index = 1;
_self.vault_history=res.data.map((d: any) => {
d.index = index;
@ -275,6 +301,7 @@ export class VaultComponent implements OnInit {
index += 1;
return d;
});
}
});
}