This commit is contained in:
crusader 2018-10-10 23:03:11 +09:00
parent 1be35047cf
commit a8422cad14
135 changed files with 3031 additions and 1810 deletions

View File

@ -98,7 +98,7 @@ export class PopupPanelComponent implements OnDestroy {
}
onOverlayAnimationDone(event: AnimationEvent) {
console.log('onOverlayAnimationDone');
}
appendOverlay() {

View File

@ -30,10 +30,6 @@ export class AutoHeightDirective implements OnInit, OnDestroy, AfterViewInit {
ngOnDestroy(): void {
}
onVisible(event) {
console.log('visibilitychange');
}
/**
* forceCalculate
*/
@ -51,7 +47,6 @@ export class AutoHeightDirective implements OnInit, OnDestroy, AfterViewInit {
const footerElementMargin = this.getfooterElementMargin();
this.elementRef.nativeElement.style.height = windowHeight - footerElementMargin - elementOffsetTop + 'px';
console.log([windowHeight, elementOffsetTop, elementMarginBottom, footerElementMargin, this.elementRef.nativeElement.style.height]);
}
private getElementOffsetTop() {

View File

@ -1,3 +1,5 @@
export * from './discovery';
export * from './DiscoverHost';
export * from './DiscoverPort';
export * from './DiscoverService';

View File

@ -1,12 +1,21 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PagesComponent } from './pages/pages.component';
import { HomePageComponent } from './pages/home/home-page.component';
const routes: Routes = [
{ path: '', loadChildren: './pages/pages.module#PagesModule' },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{
path: '',
component: PagesComponent,
children: [
{ path: 'home', component: HomePageComponent },
]
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})
export class AppRoutingModule { }

View File

@ -8,14 +8,14 @@ import { EffectsModule } from '@ngrx/effects';
import { environment } from '../environments/environment';
import { reducers, metaReducers, effects } from '../commons/store';
import { REDUCERS, META_REDUCERS, EFFECTS } from './store';
@NgModule({
exports: [
StoreModule,
],
imports: [
StoreModule.forRoot(reducers, { metaReducers }),
StoreModule.forRoot(REDUCERS, { metaReducers: META_REDUCERS }),
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
@ -41,7 +41,7 @@ import { reducers, metaReducers, effects } from '../commons/store';
maxAge: 50,
logOnly: environment.production,
}),
EffectsModule.forRoot(effects),
EffectsModule.forRoot(EFFECTS),
],
providers: [
],

View File

@ -1,18 +1,21 @@
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Component, OnInit, OnDestroy, ChangeDetectorRef, AfterContentInit, AfterViewInit } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { of, Subscription } from 'rxjs';
import { map, catchError, take } from 'rxjs/operators';
import { ElectronProxyService } from '../commons/service/electron-proxy.service';
import { ProbeService } from '../commons/service/probe.service';
import { ElectronProxyService } from './service/electron-proxy.service';
import { MenuEvent } from '../commons/type';
import * as AppStore from './store/app';
import * as UserStore from './store/environment/user';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
export class AppComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
title = 'scanner-app';
showTitleBar: boolean;
block: boolean;
@ -25,6 +28,7 @@ export class AppComponent implements OnInit, OnDestroy {
public constructor(
private changeDetector: ChangeDetectorRef,
private store: Store<any>,
private electronProxyService: ElectronProxyService,
) {
// this.showTitleBar = !__LINUX__;
@ -33,6 +37,8 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.store.dispatch(new AppStore.AppInit());
this.store.dispatch(new UserStore.SetMemberID({ memberID: 'scannerUser' }));
// this.probeService.connect();
this.electronProxyService.sendReady(performance.now());
this.menuSubscription = this.electronProxyService.menuObservable()
@ -64,7 +70,19 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngAfterContentInit(): void {
this.store.dispatch(new AppStore.AppAfterContentInit());
}
ngAfterViewInit(): void {
this.store.dispatch(new AppStore.AppAfterViewInit());
}
ngOnDestroy(): void {
this.store.dispatch(new AppStore.AppDestroy());
this.menuSubscription.unsubscribe();
}

View File

@ -4,30 +4,50 @@ import { NgModule } from '@angular/core';
import { CommonsUIModule } from '@overflow/commons/ui/commons-ui.module';
import {
PerfectScrollbarModule,
PERFECT_SCROLLBAR_CONFIG,
PerfectScrollbarConfigInterface,
} from 'ngx-perfect-scrollbar';
import { AppRoutingModule } from './app-routing.module';
import { AppStoreModule } from './app-store.module';
import { AppComponent } from './app.component';
import { COMPONENTS } from './component';
import { PAGES } from './pages';
import { SERVICES } from './service';
const DEFAULT_PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
suppressScrollX: true
};
import { CommonsModule } from '../commons/commons.module';
import { SERVICES } from '../commons/service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
PerfectScrollbarModule,
CommonsUIModule,
CommonsModule,
AppRoutingModule,
AppStoreModule,
],
declarations: [
AppComponent,
...COMPONENTS,
...PAGES,
],
providers: [
...SERVICES,
{
provide: PERFECT_SCROLLBAR_CONFIG,
useValue: DEFAULT_PERFECT_SCROLLBAR_CONFIG
}
],
bootstrap: [AppComponent]
})

View File

@ -0,0 +1,18 @@
import {
COMPONENTS as LAYOUT_COMPONENTS
} from './layout';
import {
COMPONENTS as MENU_COMPONENTS
} from './menu';
import {
COMPONENTS as TARGET_COMPONENTS
} from './target';
export const COMPONENTS = [
...LAYOUT_COMPONENTS,
...MENU_COMPONENTS,
...TARGET_COMPONENTS,
];

View File

@ -0,0 +1,8 @@
import {
COMPONENTS as TOOLBAR_COMPONENTS
} from './toolbar';
export const COMPONENTS = [
...TOOLBAR_COMPONENTS,
];

View File

@ -0,0 +1,7 @@
import { NicDropdownComponent } from './nic-dropdown.component';
import { ScannerSettingDropdownComponent } from './scanner-setting-dropdown.component';
export const COMPONENTS = [
NicDropdownComponent,
ScannerSettingDropdownComponent,
];

View File

@ -4,11 +4,11 @@
<!-- <svg _ngcontent-c1="" aria-hidden="true" class="octicon icon" version="1.1" viewBox="0 0 10 16" width="16px" height="16px">
<path _ngcontent-c1="" d="M8 1a1.993 1.993 0 0 0-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V6.5l3 3v1.78A1.993 1.993 0 0 0 5 15a1.993 1.993 0 0 0 1-3.72V9.5l3-3V4.72A1.993 1.993 0 0 0 8 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"></path>
</svg> -->
<div _ngcontent-c1="" class="text" *ngIf="selected else loading">
<div _ngcontent-c1="" class="text" *ngIf="selected">
<div _ngcontent-c1="" class="description">{{selected.network || ''}}</div>
<div _ngcontent-c1="" class="title">{{selected.friendlyName || ''}}</div>
</div>
<ng-template #loading>
<ng-template *ngIf="pending$ | async">
Loading...
</ng-template>
<svg _ngcontent-c1="" aria-hidden="true" class="octicon dropdownArrow" version="1.1" viewBox="0 0 12 16" width="16px"

View File

@ -0,0 +1,136 @@
import {
Component,
Input,
OnInit,
ViewChild,
OnDestroy,
ChangeDetectorRef
} from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map, tap, exhaustMap } from 'rxjs/operators';
import { SelectItem } from 'primeng/primeng';
import { Interface } from '@overflow/model/net/nic';
import { Zone } from '@overflow/model/discovery';
import { toMetaIPType, MetaIPType, MetaIPTypeEnum } from '@overflow/model/meta';
import { DropdownPanelComponent } from '@overflow/commons/ui/component/primeng';
import * as AppStore from '../../../store';
import * as DiscoveryConfigStore from '../../../store/discovery/config';
class NICInfo {
iface: string;
friendlyName: string;
metaIPType: MetaIPType;
mac: string;
address: string;
network: string;
}
@Component({
selector: 'app-layout-toolbar-nic-dropdown',
templateUrl: './nic-dropdown.component.html',
styleUrls: ['./nic-dropdown.component.scss']
})
export class NicDropdownComponent implements OnInit, OnDestroy {
@Input()
blockTarget: any;
@ViewChild('panel')
panel: DropdownPanelComponent;
pending$: Observable<boolean>;
error$: Observable<any>;
interfacesSubscription: Subscription | null;
nics: SelectItem[] | null;
selected: NICInfo;
constructor(
private store: Store<any>,
private changeDetector: ChangeDetectorRef,
) { }
ngOnInit(): void {
const __this = this;
this.interfacesSubscription = this.store.pipe(
tap(() => {
__this.pending$ = of(true);
}),
select(AppStore.EnvironmentSelector.InterfacesSelector.selectInterfaces),
map((ifaces: Interface[]) => {
if (null === ifaces) {
return;
}
__this.nics = [];
ifaces.forEach(iface => {
if (undefined === iface.addresses) {
return;
}
iface.addresses.forEach(address => {
if (
address.metaIPType.key !== toMetaIPType(MetaIPTypeEnum.V4).key
) {
return;
}
const nicInfo = {
iface: iface.iface,
friendlyName: iface.friendlyName,
metaIPType: address.metaIPType,
mac: iface.mac,
address: address.address,
network: address.network
};
__this.nics.push({
label: iface.friendlyName + ' (' + address.network + ')',
value: nicInfo,
disabled:
address.metaIPType.key !== toMetaIPType(MetaIPTypeEnum.V4).key
? true
: false
});
if (address.gateway !== undefined && address.gateway.length > 0) {
__this.nicSelected(nicInfo);
}
});
});
this.changeDetector.detectChanges();
}),
catchError(error => {
console.log(error);
return of();
}),
tap(() => {
__this.pending$ = of(false);
}),
).subscribe();
}
ngOnDestroy(): void {
if (null !== this.interfacesSubscription) {
this.interfacesSubscription.unsubscribe();
}
}
nicSelected(nic: NICInfo) {
this.selected = nic;
const zone: Zone = {
network: this.selected.network,
iface: this.selected.iface,
metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
address: this.selected.address,
mac: this.selected.mac
};
this.store.dispatch(new DiscoveryConfigStore.ChangeZone(zone));
this.panel.hide();
}
}

View File

@ -1,21 +1,27 @@
import { Component, Input, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
import { Component, Input, OnInit, ViewChild, Output, EventEmitter, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { Store, select } from '@ngrx/store';
import { DropdownPanelComponent } from '@overflow/commons/ui/component/primeng';
import { DiscoverHost, Zone } from '@overflow/model/discovery';
import { toMetaIPType, MetaIPTypeEnum, MetaPortTypeEnum } from '@overflow/model/meta';
import { DiscoveryConfigService } from '../../service/discovery-config.service';
import * as AppStore from '../../../store';
import * as DiscoveryConfigStore from '../../../store/discovery/config';
const IPCIDR = require('ip-cidr');
@Component({
selector: 'app-scanner-setting-dropdown',
selector: 'app-layout-toolbar-scanner-setting-dropdown',
templateUrl: './scanner-setting-dropdown.component.html',
styleUrls: ['./scanner-setting-dropdown.component.scss'],
})
export class ScannerSettingDropdownComponent implements OnInit {
export class ScannerSettingDropdownComponent implements OnInit, OnDestroy {
@Input() blockTarget: any;
@Output() ready = new EventEmitter<DiscoverHost>();
@ViewChild('panel') panel: DropdownPanelComponent;
valid: boolean;
@ -35,17 +41,36 @@ export class ScannerSettingDropdownComponent implements OnInit {
validIPArray: string[];
zoneSubscription: Subscription | null;
constructor(
private discoveryConfigService: DiscoveryConfigService
private store: Store<any>,
private changeDetector: ChangeDetectorRef,
) {
}
ngOnInit(): void {
this.discoveryConfigService.zone.subscribe(res => {
this.zone = res as Zone;
this.setDefault();
});
this.zoneSubscription = this.store.pipe(
select(AppStore.DiscoverySelector.ConfigSelector.selectZone),
map((zone: Zone) => {
if (null === zone) {
return;
}
this.zone = zone;
this.setDefault();
}),
catchError(error => {
console.log(error);
return of();
}),
).subscribe();
}
ngOnDestroy(): void {
if (null !== this.zoneSubscription) {
this.zoneSubscription.unsubscribe();
}
}
setDefault(): void {
@ -66,6 +91,8 @@ export class ScannerSettingDropdownComponent implements OnInit {
this.portErrMsg = '';
this.setSummary();
this.changeDetector.detectChanges();
}
validateIP(value: string, idx: number) {
@ -212,9 +239,9 @@ export class ScannerSettingDropdownComponent implements OnInit {
}
}
};
this.ready.emit(discoverHost);
}
this.store.dispatch(new DiscoveryConfigStore.ChangeDiscoverHost(discoverHost));
}
}
class Condition {

View File

@ -0,0 +1,11 @@
import { AboutComponent } from './about.component';
import { ExportCSVComponent } from './export-csv.component';
import { PreferencesComponent } from './preferences.component';
import { PrintComponent } from './print.component';
export const COMPONENTS = [
AboutComponent,
ExportCSVComponent,
PreferencesComponent,
PrintComponent,
];

View File

@ -6,16 +6,16 @@ import { map, catchError, take } from 'rxjs/operators';
import { Host } from '@overflow/model/discovery';
import { PingResult } from '@overflow/model/ping';
import { ProbeService } from '../../service/probe.service';
import { AutoHeightDirective } from '@overflow/commons/ui/directive/auto-height.directive';
import { PingService } from '../../../service/ping.service';
import { PingOption } from '@overflow/model/config/ping';
@Component({
selector: 'app-host-detail',
templateUrl: './host-detail.component.html',
styleUrls: ['./host-detail.component.scss'],
selector: 'app-target-detail-host',
templateUrl: './host.component.html',
styleUrls: ['./host.component.scss'],
})
export class HostDetailComponent implements OnChanges {
export class HostComponent implements OnChanges {
@Input() host: Host;
@ViewChildren(AutoHeightDirective) autoHeightDirectives: QueryList<AutoHeightDirective>;
@ -32,7 +32,7 @@ export class HostDetailComponent implements OnChanges {
constructor(
private probeService: ProbeService,
private pingService: PingService,
) {
this.pingWaiting = false;
this.count = 5;
@ -48,14 +48,13 @@ export class HostDetailComponent implements OnChanges {
doPing() {
this.pingWaiting = true;
const option = {
Count: this.count,
Interval: this.interval,
Deadline: this.deadline,
const option: PingOption = {
count: this.count,
interval: this.interval,
deadline: this.deadline,
};
this.probeService
.call<PingResult>('PingService.PingHost', this.host, option)
this.pingService.pingHost(this.host, option)
.pipe(
map((pingResult: PingResult) => {
if (pingResult) {

View File

@ -0,0 +1,11 @@
import { HostComponent } from './host.component';
import { NodeComponent } from './node.component';
import { ServiceComponent } from './service.component';
import { ZoneComponent } from './zone.component';
export const COMPONENTS = [
NodeComponent,
ZoneComponent,
HostComponent,
ServiceComponent,
];

View File

@ -0,0 +1,26 @@
<div class="ui-g">
<div class="ui-g-12">
<ng-container [ngSwitch]="selectedTarget.group">
<ng-container *ngSwitchCase="'zone'">
<app-target-detail-zone [zone]="selectedTarget.target" (otherHostSelect)="otherHostSelected($event)"></app-target-detail-zone>
</ng-container>
<ng-container *ngSwitchCase="'host'">
<app-target-detail-host [host]="selectedTarget.target" (ping)="ping.emit($event)"></app-target-detail-host>
</ng-container>
<ng-container *ngSwitchCase="'service'">
<app-target-detail-service [service]="selectedTarget.target" (ping)="ping.emit($event)"></app-target-detail-service>
</ng-container>
<ng-container *ngSwitchDefault>
</ng-container>
</ng-container>
</div>
</div>

View File

@ -0,0 +1,26 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { PingResult } from '@overflow/model/ping';
import { Host, Port, Service } from '@overflow/model/discovery';
@Component({
selector: 'app-target-detail-node',
templateUrl: './node.component.html',
styleUrls: ['./node.component.scss'],
})
export class NodeComponent {
@Input() selectedTarget: { group: string, target: Host | Port | Service } | null;
@Output() otherHostSelect = new EventEmitter<Host>();
@Output() ping = new EventEmitter<PingResult>();
constructor(
) {
}
otherHostSelected(host: Host) {
this.otherHostSelect.emit(host);
}
}

View File

@ -1,17 +1,21 @@
import { Component, Input, OnChanges, SimpleChanges, ViewChildren, QueryList } from '@angular/core';
import { Service } from '@overflow/model/discovery';
import { PingResult } from '@overflow/model/ping';
import { ProbeService } from '../../service/probe.service';
import { map, catchError, take } from 'rxjs/operators';
import { of } from 'rxjs';
import { map, catchError, take } from 'rxjs/operators';
import { Service } from '@overflow/model/discovery';
import { AutoHeightDirective } from '@overflow/commons/ui/directive/auto-height.directive';
import { PingResult } from '@overflow/model/ping';
import { PingService } from '../../../service/ping.service';
import { PingOption } from '@overflow/model/config/ping';
@Component({
selector: 'app-service-detail',
templateUrl: './service-detail.component.html',
styleUrls: ['./service-detail.component.scss'],
selector: 'app-target-detail-service',
templateUrl: './service.component.html',
styleUrls: ['./service.component.scss'],
})
export class ServiceDetailComponent implements OnChanges {
export class ServiceComponent implements OnChanges {
@Input() service: Service;
@ -28,7 +32,7 @@ export class ServiceDetailComponent implements OnChanges {
constructor(
private probeService: ProbeService
private pingService: PingService
) {
this.pingWaiting = false;
this.count = 5;
@ -44,14 +48,13 @@ export class ServiceDetailComponent implements OnChanges {
doPing() {
this.pingWaiting = true;
const option = {
Count: this.count,
Interval: this.interval,
Deadline: this.deadline,
const option: PingOption = {
count: this.count,
interval: this.interval,
deadline: this.deadline,
};
this.probeService
.call<PingResult>('PingService.PingService', this.service, option)
this.pingService.pingService(this.service, option)
.pipe(
map((pingResult: PingResult) => {
console.log(pingResult);

View File

@ -5,11 +5,11 @@ import { AutoHeightDirective } from '@overflow/commons/ui/directive/auto-height.
const IPCIDR = require('ip-cidr');
@Component({
selector: 'app-zone-detail',
templateUrl: './zone-detail.component.html',
styleUrls: ['./zone-detail.component.scss'],
selector: 'app-target-detail-zone',
templateUrl: './zone.component.html',
styleUrls: ['./zone.component.scss'],
})
export class ZoneDetailComponent implements OnInit {
export class ZoneComponent implements OnInit {
@Input() zone: Zone;
@Output() otherHostSelect = new EventEmitter<Host>();

View File

@ -0,0 +1,18 @@
<div>
<ng-container [ngSwitch]="(targetDisplayType$ | async)">
<app-target-display-map *ngSwitchCase="'MAP'"></app-target-display-map>
<app-target-display-grid *ngSwitchCase="'GRID'"></app-target-display-grid>
<app-target-display-tree *ngSwitchCase="'TREE'"></app-target-display-tree>
</ng-container>
<div #detailSidebar style="position: inherit; top: 0; left: 0; height: 100%;"></div>
<p-sidebar [(visible)]="displaySidebar" [modal]="false" [appendTo]="detailSidebar" styleClass="ui-sidebar-md"
position="right" (onHide)="onHideDetail()" [style]="{position:'absolute'}">
<div *ngIf="selectedTarget">
<app-target-detail-node [selectedTarget]="selectedTarget" (otherHostSelect)="otherHostSelected($event)"></app-target-detail-node>
</div>
</p-sidebar>
</div>

View File

@ -0,0 +1,21 @@
/deep/ .target-display {
width: 100%;
height: 100vh;
margin: -0.6em -0.9em -0.7em -0.9em; //-0.5em -0.75em;
padding: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
min-width: 400px; //text-align: center;
}
.detail-sidebar {
top: 0;
left: 0;
height: 100%;
}
/deep/ .ui-panel .ui-panel-content {
padding: .6em .75em;
}

View File

@ -0,0 +1,74 @@
import {
Component,
OnInit,
OnDestroy,
AfterContentInit,
ChangeDetectorRef
} from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { map, catchError, take, tap } from 'rxjs/operators';
import { Port, Service, Host } from '@overflow/model/discovery';
import { TargetDisplayType } from '../../../core/type';
import * as AppStore from '../../../store';
import * as TargetStore from '../../../store/target/target';
@Component({
selector: 'app-target-display',
templateUrl: './display.component.html',
styleUrls: ['./display.component.scss']
})
export class DisplayComponent implements OnInit, AfterContentInit, OnDestroy {
private targetDisplayType$: Observable<TargetDisplayType>;
displaySidebar = false;
selectedTargetSubscription: Subscription | null;
selectedTarget: { group: string, target: Host | Port | Service } | null;
constructor(
private store: Store<any>,
private changeDetector: ChangeDetectorRef,
) {
}
ngOnInit(): void {
const __this = this;
this.targetDisplayType$ = this.store.pipe(select(AppStore.TargetSelector.TargetSelector.selectTargetDisplayType));
this.selectedTargetSubscription = this.store.pipe(
select(AppStore.TargetSelector.TargetSelector.selectSelectedTarget)
).pipe(
map((_selectedTarget) => {
if (null === _selectedTarget) {
__this.displaySidebar = false;
__this.selectedTarget = null;
} else {
__this.displaySidebar = true;
__this.selectedTarget = _selectedTarget;
}
__this.changeDetector.detectChanges();
}),
).subscribe();
}
ngAfterContentInit(): void {
}
ngOnDestroy(): void {
if (null !== this.selectedTargetSubscription) {
this.selectedTargetSubscription.unsubscribe();
}
}
onHideDetail() {
this.store.dispatch(new TargetStore.ChangeSelectedTarget(null));
}
}

View File

@ -0,0 +1,3 @@
<div>
Grid
</div>

View File

@ -0,0 +1,31 @@
import {
Component,
OnInit,
OnDestroy,
AfterContentInit
} from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'app-target-display-grid',
templateUrl: './grid.component.html',
styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit, AfterContentInit, OnDestroy {
constructor(
private store: Store<any>,
) {
}
ngOnInit(): void {
}
ngAfterContentInit(): void {
}
ngOnDestroy(): void {
}
}

View File

@ -0,0 +1,11 @@
import { DisplayComponent } from './display.component';
import { GridComponent } from './grid.component';
import { MapComponent } from './map.component';
import { TreeComponent } from './tree.component';
export const COMPONENTS = [
DisplayComponent,
GridComponent,
MapComponent,
TreeComponent,
];

View File

@ -1,5 +1,5 @@
<div>
<svg #discoveryTarget width="100%" height="100%">
<div class="target-display">
<svg #displayTarget width="100%" height="100%">
<g>
<g *ngFor="let link of links">
<g class="link-container" [ngClass]="'link-'+link.target.group" [attr.sourceId]="link.source.id"
@ -51,15 +51,18 @@
</g>
</g>
</svg>
<p-card class="ui-map-info" *ngIf="discoveryResult">
<p-card class="ui-map-info" *ngIf="displaySummary">
<div class="ui-map-info-row ui-border-bottom">
<div>Total Hosts: </div>{{discoveryResult.totalHosts}}
<div>Total Hosts: </div>{{displaySummary.totalHosts}}
</div>
<div class="ui-map-info-row ui-border-bottom">
<div>Total Services: </div>{{discoveryResult.totalServices}}
<div>Total Ports: </div>{{displaySummary.totalPorts}}
</div>
<div class="ui-map-info-row ui-border-bottom">
<div>Total Services: </div>{{displaySummary.totalServices}}
</div>
<div class="ui-map-info-row">
<div>Elapsed: </div>{{discoveryResult.elapsedTime}}
<div>Elapsed: </div>{{displaySummary.elapsedTime}}
</div>
</p-card>
</div>

View File

@ -0,0 +1,83 @@
.link {
stroke: #999;
stroke-opacity: 0.6;
}
.textClass {
stroke: #323232;
font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
font-weight: normal;
stroke-width: .5;
font-size: 14px;
}
.linkTextClass {
stroke: #b6b4b4;
font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
font-weight: normal;
stroke-width: .3;
font-size: 9px;
}
.focused {
opacity: 1 !important;
}
.semi-focused {
opacity: 0.8 !important;
}
.semi-unfocused {
opacity: 0.8;
}
.unfocused {
opacity: 0.3;
}
.semi-unselected {
opacity: 0.8;
}
.unselected {
opacity: 0.3;
}
.ui-map-info {
position: absolute;
top: 10px;
left: 10px;
.ui-map-info-row {
//display: inline;
font-size: 0.8em;
line-height: 2em;
width: 120px;
font-weight: bold;
user-select: text;
div {
display: inline-block;
font-weight: normal;
padding-left: 5px;
width: 75px;
user-select: text;
}
}
.ui-border-bottom {
border-bottom: 1px solid #d6d6d6;
}
}
/deep/ .ui-card-body {
padding: 0.5em;
border: 1px solid #d6d6d6;
border-radius: 3px;
}
// /deep/ .ui-blockui {
// opacity: 0.1;
// }
/deep/ .ui-widget-overlay {
background-color: #666666;
opacity: .20;
filter: Alpha(Opacity=50);
}

View File

@ -0,0 +1,820 @@
import {
Component,
Input,
OnInit,
EventEmitter,
Output,
ViewChild,
OnDestroy,
ElementRef,
AfterContentInit,
ChangeDetectorRef,
HostListener
} from '@angular/core';
import { Observable, of, Subscription, combineLatest } from 'rxjs';
import { catchError, map, tap, exhaustMap, take, filter } from 'rxjs/operators';
import { Store, select } from '@ngrx/store';
import * as d3 from 'd3';
import { Zone, Host, Service, Port } from '@overflow/model/discovery';
import { Link } from '../../../core/model/link';
import { Node } from '../../../core/model/node';
import * as AppStore from '../../../store';
import * as DiscoveryConfigStore from '../../../store/discovery/config';
import * as TargetStore from '../../../store/target/target';
import * as UILayoutStore from '../../../store/ui/layout';
import { DiscoverySession } from '../../../core/discovery/discovery-session';
import { DiscoveryMessageType } from 'src/app/core/type';
export class DisplaySummary {
totalHosts: number;
totalPorts: number;
totalServices: number;
elapsedTime: string;
startDate: Date;
hTimer: any;
constructor() {
this.totalHosts = 0;
this.totalPorts = 0;
this.totalServices = 0;
this.elapsedTime = '00:00:00';
}
start(startDate: Date) {
this.startDate = startDate;
const __this = this;
const _startDate = new Date();
this.hTimer = setInterval(function () {
// Get todays date and time
const now = new Date().getTime();
__this.setElapsedTime(_startDate, new Date());
}, 1000);
}
setElapsedTime(startDate: Date, now: Date) {
const distance = now.getTime() - startDate.getTime();
// Time calculations for days, hours, minutes and seconds
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
this.elapsedTime = String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0') + ':' + String(seconds).padStart(2, '0');
}
stop(stopDate: Date) {
clearInterval(this.hTimer);
this.setElapsedTime(this.startDate, stopDate);
}
increaseHost() {
this.totalHosts++;
}
increasePort() {
this.totalPorts++;
}
increaseService() {
this.totalServices++;
}
}
@Component({
selector: 'app-target-display-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.scss']
})
export class MapComponent implements OnInit, AfterContentInit, OnDestroy {
zone: Zone | null = null;
hosts: Map<string, Host>;
ports: Map<string, Map<number, Port>>;
services: Map<string, Map<number, Map<string, Service>>>;
zoneNode: Node | null = null;
nodes: Node[];
links: Link[];
selectedNode: Node | null = null;
@ViewChild('displayTarget') set displayTarget(content: ElementRef) {
this.displayTargetRef = content;
}
private displayTargetRef: ElementRef;
public simulation: d3.Simulation<Node, Link> | undefined;
private zoomBehavior: d3.ZoomBehavior<Element, {}>;
private displayTargetWidth: number;
private displayTargetHeight: number;
private readonly maxScale: number;
private readonly minScale: number;
displaySummary: DisplaySummary | null = null;
discoverySessionSubscription: Subscription | null = null;
refreshTargetDisplaySubscription: Subscription | null = null;
targetSubscription: Subscription | null = null;
targetObservable: Observable<void>;
selectedTargetSubscription: Subscription | null = null;
discoverySubscription: Subscription | null = null;
constructor(
private store: Store<any>,
private changeDetector: ChangeDetectorRef,
) {
this.maxScale = 1;
this.minScale = 0.7;
const __this = this;
this.targetObservable = this.store.pipe(
take(1),
select((state) => {
const zone = AppStore.DiscoverySelector.ConfigSelector.selectZone(state);
const hosts = AppStore.TargetSelector.TargetSelector.selectHosts(state);
const ports = AppStore.TargetSelector.TargetSelector.selectPorts(state);
const services = AppStore.TargetSelector.TargetSelector.selectServices(state);
__this.initMapDisplay(zone, hosts, ports, services);
}),
);
}
@HostListener('window:resize', ['$event'])
onResize(event) {
if (undefined !== this.displayTargetRef) {
this.displayTargetWidth = this.displayTargetRef.nativeElement.clientWidth;
this.displayTargetHeight = this.displayTargetRef.nativeElement.clientHeight;
this.zoneNode.fx = this.displayTargetWidth / 2;
this.zoneNode.fy = this.displayTargetHeight / 2;
this.simulationRestart(false);
}
}
ngOnInit(): void {
const __this = this;
this.discoverySessionSubscription = this.store.pipe(
select(AppStore.DiscoverySelector.RequestSelector.selectDiscoverySession),
map((discoverySession: DiscoverySession) => {
if (null === discoverySession) {
if (null !== __this.discoverySubscription) {
__this.discoverySubscription.unsubscribe();
__this.discoverySubscription = null;
}
return;
}
__this.discoverySubscription = discoverySession.discoveryObservable().pipe(
map((discoveryMessage: { messageType: DiscoveryMessageType, params: any[] }) => {
switch (discoveryMessage.messageType) {
case DiscoveryMessageType.Queueing:
break;
case DiscoveryMessageType.QueueingFailed:
break;
case DiscoveryMessageType.QueueingTimeout:
break;
case DiscoveryMessageType.DiscoveryStart: {
__this.store.dispatch(new UILayoutStore.ChangeBlockPagesContent({ blockPagesContent: true }));
__this.initMapDisplay(__this.zone, null, null, null);
__this.displaySummary.start(discoveryMessage.params[0]);
}
break;
case DiscoveryMessageType.DiscoveryStop: {
__this.simulationRestart(true);
__this.zoomToFit(0.95, 500);
__this.displaySummary.stop(discoveryMessage.params[0]);
__this.store.dispatch(new UILayoutStore.ChangeBlockPagesContent({ blockPagesContent: false }));
}
break;
case DiscoveryMessageType.DiscoveryMode:
break;
case DiscoveryMessageType.DiscoveryError:
break;
case DiscoveryMessageType.DiscoveredHost:
__this.addHost(discoveryMessage.params[0], true);
break;
case DiscoveryMessageType.DiscoveredPort:
__this.addPort(discoveryMessage.params[0], false);
break;
case DiscoveryMessageType.DiscoveredService:
__this.addService(discoveryMessage.params[0], true);
break;
default:
break;
}
}),
).subscribe();
}),
catchError(error => {
console.log(error);
return of();
}),
).subscribe();
this.refreshTargetDisplaySubscription = this.store.pipe(
select(AppStore.TargetSelector.TargetSelector.selectRefreshTargetDisplay),
filter(v => !!v),
map((refreshTargetDisplay: boolean) => {
if (!refreshTargetDisplay) {
return;
}
__this.targetObservable.subscribe();
}),
catchError(error => {
console.log(error);
return of();
}),
).subscribe();
this.selectedTargetSubscription = this.store.pipe(
select(AppStore.TargetSelector.TargetSelector.selectSelectedTarget)
).pipe(
map((selectedTarget) => {
if (null === selectedTarget) {
__this.onHideDetail();
return;
} else {
}
}),
).subscribe();
}
ngAfterContentInit(): void {
this.displayTargetWidth = this.displayTargetRef.nativeElement.clientWidth;
this.displayTargetHeight = this.displayTargetRef.nativeElement.clientHeight;
this.targetObservable.subscribe();
}
ngOnDestroy(): void {
if (null !== this.targetSubscription) {
this.targetSubscription.unsubscribe();
}
if (null !== this.refreshTargetDisplaySubscription) {
this.refreshTargetDisplaySubscription.unsubscribe();
}
if (null !== this.discoverySessionSubscription) {
this.discoverySessionSubscription.unsubscribe();
}
if (null !== this.selectedTargetSubscription) {
this.selectedTargetSubscription.unsubscribe();
}
}
initMapDisplay(zone: Zone, hosts: Host[], ports: Port[], services: Service[]) {
if (null === zone) {
return;
}
this.nodes = [];
this.links = [];
this.hosts = new Map();
this.ports = new Map();
this.services = new Map();
this.displaySummary = new DisplaySummary();
this.simulationInit();
this.setZone(zone);
if (null !== hosts) {
hosts.forEach((host) => {
this.addHost(host);
});
}
if (null !== ports) {
ports.forEach((port) => {
this.addPort(port);
});
}
if (null !== services) {
services.forEach((service) => {
this.addService(service);
});
}
this.simulationRestart(true);
}
simulationInit() {
if (undefined !== this.simulation) {
return;
}
const svg = d3.select(this.displayTargetRef.nativeElement);
this.zoomBehavior = d3.zoom()
.scaleExtent([0.2, 4])
.on('zoom', () => {
const transform = d3.event.transform;
svg.select('g').attr('transform', 'translate(' + transform.x + ',' + transform.y + ') scale(' + transform.k + ')');
});
svg.call(this.zoomBehavior);
const __this = this;
this.simulation = d3
.forceSimulation<Node, Link>()
.nodes(this.nodes)
.force('charge', d3.forceManyBody().strength(-200))
.force('link', d3.forceLink(this.links).distance(150))
.force('center', d3.forceCenter(this.displayTargetWidth / 2, this.displayTargetHeight / 2))
.force('collision', d3.forceCollide().radius(function (node: Node) {
return node.r * 1.2;
}))
.on('tick', () => {
__this.changeDetector.markForCheck();
})
;
}
simulationRestart(attachEvent: boolean = false) {
// Update and restart the simulation.
this.simulation
.nodes(this.nodes)
.force('link', d3.forceLink(this.links).distance(150))
.force('center', d3.forceCenter(this.displayTargetWidth / 2, this.displayTargetHeight / 2))
.alpha(1)
.restart()
;
this.changeDetector.detectChanges();
if (attachEvent) {
const __this = this;
const nodeElements = d3.select(this.displayTargetRef.nativeElement).selectAll('.node-container');
const linkElements = d3.select(this.displayTargetRef.nativeElement).selectAll('.link-container');
function getNodeFromElement(element: Element): Node | null {
const container = d3.select(element);
const nodeId = container.attr('nodeId');
return __this.getNode(nodeId);
}
// drag
nodeElements.call(
d3.drag()
.on('start', function () {
const node = getNodeFromElement(this);
if (null === node || 'zone' === node.group) {
return;
}
d3.event.sourceEvent.stopPropagation();
if (!d3.event.active) {
__this.simulation.alphaTarget(0.3).restart();
}
d3.event.on('drag', dragged).on('end', ended);
function dragged(d, i) {
node.fx = d3.event.x;
node.fy = d3.event.y;
}
function ended() {
if (!d3.event.active) {
__this.simulation.alphaTarget(0);
}
node.fx = null;
node.fy = null;
}
})
);
enum ConnectedNode {
TARGET = 1,
CONNECTED,
NOT_CONNECTED,
}
function isConnectedNode(node: Node, element: Element): ConnectedNode {
const _node = getNodeFromElement(element);
if (node === _node) {
return ConnectedNode.TARGET;
}
for (let i = 0; i < node.links.length; i++) {
const link = node.links[i];
if (node === _node || link.source === _node || link.target === _node) {
return ConnectedNode.CONNECTED;
}
}
return ConnectedNode.NOT_CONNECTED;
}
function isConnectedLink(node: Node, element: Element) {
const _linkElement = d3.select(element);
const sourceId = _linkElement.attr('sourceId');
const targetId = _linkElement.attr('targetId');
if (sourceId === node.id || targetId === node.id) {
return true;
}
return false;
}
nodeElements.on('click', function () {
d3.event.stopPropagation();
const nodeElement = this as Element;
const node = getNodeFromElement(nodeElement);
if (null === node || 'zone' === node.group) {
__this.onTargetClick(node);
return;
}
if (null === __this.selectedNode) {
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unfocused', false);
d3.select(_thisElement).classed('unfocused', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unfocused', false);
d3.select(_thisElement).classed('unfocused', false);
});
} else {
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
}
__this.selectedNode = node;
nodeElements.each(function () {
const _thisElement = this as Element;
switch (isConnectedNode(node, _thisElement)) {
case ConnectedNode.CONNECTED:
d3.select(_thisElement).classed('semi-unselected', true);
break;
case ConnectedNode.NOT_CONNECTED:
d3.select(_thisElement).classed('unselected', true);
break;
default:
break;
}
});
linkElements.each(function () {
const _thisElement = this as Element;
if (isConnectedLink(node, _thisElement)) {
d3.select(_thisElement).classed('semi-unselected', true);
} else {
d3.select(_thisElement).classed('unselected', true);
}
});
__this.onTargetClick(node);
});
// Highlight
const displayTarget = d3.select(this.displayTargetRef.nativeElement);
displayTarget.on('click', function () {
__this.selectedNode = null;
__this.store.dispatch(new TargetStore.ChangeSelectedTarget(null));
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
});
nodeElements
.on('mouseover', function () {
const node = getNodeFromElement(this as Element);
if ('zone' === node.group) {
return;
}
if (null === __this.selectedNode) {
nodeElements.each(function () {
const _thisElement = this as Element;
switch (isConnectedNode(node, _thisElement)) {
case ConnectedNode.CONNECTED:
d3.select(_thisElement).classed('semi-unfocused', true);
break;
case ConnectedNode.NOT_CONNECTED:
d3.select(_thisElement).classed('unfocused', true);
break;
default:
break;
}
});
linkElements.each(function () {
const _thisElement = this as Element;
if (isConnectedLink(node, _thisElement)) {
d3.select(_thisElement).classed('semi-unfocused', true);
} else {
d3.select(_thisElement).classed('unfocused', true);
}
});
} else {
nodeElements.each(function () {
const _thisElement = this as Element;
switch (isConnectedNode(node, _thisElement)) {
case ConnectedNode.TARGET:
d3.select(_thisElement).classed('focused', true);
break;
case ConnectedNode.CONNECTED:
d3.select(_thisElement).classed('semi-focused', true);
break;
default:
break;
}
});
linkElements.each(function () {
const _thisElement = this as Element;
if (isConnectedLink(node, _thisElement)) {
d3.select(_thisElement).classed('semi-focused', true);
}
});
}
})
.on('mouseout', function () {
const node = getNodeFromElement(this as Element);
if (null === __this.selectedNode) {
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unfocused', false);
d3.select(_thisElement).classed('unfocused', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unfocused', false);
d3.select(_thisElement).classed('unfocused', false);
});
} else {
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-focused', false);
d3.select(_thisElement).classed('focused', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-focused', false);
d3.select(_thisElement).classed('focused', false);
});
}
})
;
}
}
zoomToFit(paddingPercent, transitionDuration) {
const root = d3.select(this.displayTargetRef.nativeElement);
const bounds = root.node().getBBox();
const fullWidth = this.displayTargetWidth;
const fullHeight = this.displayTargetHeight;
const width = bounds.width;
const height = bounds.height;
const midX = bounds.x + width / 2;
const midY = bounds.y + height / 2;
if (width === 0 || height === 0) { return; } // nothing to fit
let scale = (paddingPercent || 0.75) / Math.max(width / fullWidth, height / fullHeight);
console.log(`scale: ${scale}`);
if (this.maxScale < scale) {
scale = this.maxScale;
}
if (this.minScale > scale) {
scale = this.minScale;
}
const translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];
root
.transition()
.duration(transitionDuration || 0) // milliseconds
.call(this.zoomBehavior.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale));
}
onTargetClick(node: Node) {
console.log(node);
switch (node.group) {
case 'zone':
const zone: Zone = node.target;
zone.hostList = [];
this.hosts.forEach(_host => {
if (_host.zone.network === zone.network) {
zone.hostList.push(_host);
}
});
break;
case 'host':
const host: Host = node.target;
host.portList = [];
if (this.ports.has(host.address)) {
this.ports.get(host.address).forEach(port => {
host.portList.push(port);
});
}
break;
default:
break;
}
this.store.dispatch(new TargetStore.ChangeSelectedTarget({
group: node.group,
target: node.target,
}));
this.selectedNode = node;
}
onHideDetail() {
const __this = this;
const nodeElements = d3.select(this.displayTargetRef.nativeElement).selectAll('.node-container');
const linkElements = d3.select(this.displayTargetRef.nativeElement).selectAll('.link-container');
__this.selectedNode = null;
nodeElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
linkElements.each(function () {
const _thisElement = this as Element;
d3.select(_thisElement).classed('semi-unselected', false);
d3.select(_thisElement).classed('unselected', false);
});
}
private getNode(id: string): Node | null {
let _n: Node = null;
this.nodes.some((node): boolean => {
if (node.id === id) {
_n = node;
return true;
}
return false;
});
return _n;
}
refreshTargetDisplay(zone: Zone, hosts: Host[], services: Service[]) {
}
setZone(zone: Zone, requireRefresh: boolean = false) {
if (null === zone) {
return;
}
this.zone = zone;
this.zoneNode = new Node(zone.network);
this.zoneNode.group = 'zone';
this.zoneNode.target = zone;
this.zoneNode.fx = this.displayTargetWidth / 2;
this.zoneNode.fy = this.displayTargetHeight / 2;
this.zoneNode.r = 60;
this.nodes.push(
this.zoneNode,
);
if (requireRefresh) {
this.simulationRestart();
}
}
addHost(host: Host, requireRefresh: boolean = false) {
if (null === host) {
return;
}
this.hosts.set(host.address, host);
const hostId = `${host.address}`;
let hostNode = this.getNode(hostId);
if (null !== hostNode) {
hostNode.target = host;
} else {
hostNode = new Node(hostId);
hostNode.target = host;
hostNode.group = 'host';
hostNode.r = 40;
hostNode.x = this.zoneNode.x;
hostNode.y = this.zoneNode.y;
this.nodes.push(hostNode);
this.links.push(new Link(this.zoneNode, hostNode));
this.displaySummary.increaseHost();
if (requireRefresh) {
this.simulationRestart();
}
}
}
addPort(port: Port, requireRefresh: boolean = false) {
if (null === port) {
return;
}
let _ports: Map<number, Port>;
if (!this.ports.has(port.host.address)) {
_ports = new Map();
this.ports.set(port.host.address, _ports);
} else {
_ports = this.ports.get(port.host.address);
}
if (!_ports.has(port.portNumber)) {
this.displaySummary.increasePort();
}
_ports.set(port.portNumber, port);
if (requireRefresh) {
this.simulationRestart();
}
}
addService(service: Service, requireRefresh: boolean = false) {
if (null === service) {
return;
}
const hostId = `${service.port.host.address}`;
const serviceId = `${service.port.host.address}-${service.port.portNumber}-${service.port.metaPortType.key}`;
const hostNode = this.getNode(hostId);
let serviceNode = this.getNode(serviceId);
if (null !== serviceNode) {
serviceNode.target = service;
} else {
serviceNode = new Node(serviceId);
serviceNode.target = service;
serviceNode.group = 'service';
serviceNode.r = 30;
serviceNode.x = hostNode.x;
serviceNode.y = hostNode.y;
this.nodes.push(serviceNode);
this.links.push(new Link(hostNode, serviceNode));
this.displaySummary.increaseService();
if (requireRefresh) {
this.simulationRestart();
}
}
}
}

View File

@ -0,0 +1,3 @@
<div>
Tree
</div>

View File

@ -0,0 +1,31 @@
import {
Component,
OnInit,
OnDestroy,
AfterContentInit
} from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'app-target-display-tree',
templateUrl: './tree.component.html',
styleUrls: ['./tree.component.scss']
})
export class TreeComponent implements OnInit, AfterContentInit, OnDestroy {
constructor(
private store: Store<any>,
) {
}
ngOnInit(): void {
}
ngAfterContentInit(): void {
}
ngOnDestroy(): void {
}
}

View File

@ -0,0 +1,13 @@
import {
COMPONENTS as DETAIL_COMPONENTS
} from './detail';
import {
COMPONENTS as DISPLAY_COMPONENTS
} from './display';
export const COMPONENTS = [
...DETAIL_COMPONENTS,
...DISPLAY_COMPONENTS,
];

View File

@ -0,0 +1,230 @@
import { Store, select } from '@ngrx/store';
import { Host, Port, DiscoveryModeType, Service } from '@overflow/model/discovery';
import { RPCSubscriber } from '@overflow/commons/ui/decorator/RPCSubscriber';
import { RPCError } from '@overflow/rpc-js';
import { ProbeService } from '../../service/probe.service';
import { DiscoverRequestInfo } from '../model';
import * as AppStore from '../../store';
import * as DiscoveryRequestStore from '../../store/discovery/request';
import * as UserStore from '../../store/environment/user';
import * as TargetTargetStore from '../../store/target/target';
import { take, map } from 'rxjs/operators';
import { DiscoveryMessageType, TargetDisplayType, DiscoveryStatusType } from '../type';
import { Subject, Observable } from 'rxjs';
export class DiscoverySession {
private _discoverRequestInfo: DiscoverRequestInfo | null;
private _requestID: string | null;
private startDate: Date | null;
private discoverySubject: Subject<{ messageType: DiscoveryMessageType, params: any[] }> | null;
private constructor(
private store: Store<any>,
private probeService: ProbeService,
) {
}
public static requestDiscover(store: Store<any>, probeService: ProbeService): void {
store.pipe(
take(1),
select((state) => {
const memberID = AppStore.EnvironmentSelector.UserSelector.selectMemberID(state);
const zone = AppStore.DiscoverySelector.ConfigSelector.selectZone(state);
const host = AppStore.DiscoverySelector.ConfigSelector.selectHost(state);
const port = AppStore.DiscoverySelector.ConfigSelector.selectPort(state);
const discoverHost = AppStore.DiscoverySelector.ConfigSelector.selectDiscoverHost(state);
const discoverPort = AppStore.DiscoverySelector.ConfigSelector.selectDiscoverPort(state);
const discoverService = AppStore.DiscoverySelector.ConfigSelector.selectDiscoverService(state);
const discoverySession = AppStore.DiscoverySelector.RequestSelector.selectDiscoverySession(state);
if (null !== discoverySession) {
return null;
}
const discoverRequestInfo = {
requesterID: memberID,
zone,
host,
port,
discoverHost,
discoverPort,
discoverService,
};
const _discoverySession = new DiscoverySession(store, probeService);
_discoverySession.init(discoverRequestInfo);
return _discoverySession;
}),
).pipe(
map((discoverySession) => {
if (null === discoverySession) {
return;
}
store.dispatch(new UserStore.ChangeShowWelcomPage({ showWelcomPage: false }));
store.dispatch(new TargetTargetStore.ChangeTargetDisplayType({ targetDisplayType: TargetDisplayType.MAP }));
store.dispatch(new DiscoveryRequestStore.RequestDiscover({ discoverySession }));
// store.dispatch(new UserStore.ChangeShowWelcomPage({ showWelcomPage: false }));
// store.dispatch(new TargetTargetStore.ChangeTargetDisplayType({ targetDisplayType: TargetDisplayType.MAP }));
}),
).subscribe();
}
public static requestDiscoverStop(store: Store<any>): void {
store.pipe(
take(1),
select((state) => {
const discoverySession = AppStore.DiscoverySelector.RequestSelector.selectDiscoverySession(state);
if (null === discoverySession) {
return null;
}
return discoverySession;
}),
).pipe(
map((discoverySession) => {
if (null === discoverySession) {
return;
}
store.dispatch(new DiscoveryRequestStore.RequestDiscoverStop({ discoverySession }));
}),
).subscribe();
}
init(discoverRequestInfo: DiscoverRequestInfo) {
this._discoverRequestInfo = discoverRequestInfo;
this.discoverySubject = new Subject();
this.probeService.subscribeNotification(this);
}
destroy(requireDispatch: boolean = true) {
this.probeService.unsubscribeNotification(this);
if (requireDispatch) {
this.store.dispatch(new DiscoveryRequestStore.DiscoveryComplete());
}
this._requestID = null;
this.startDate = null;
this._discoverRequestInfo = null;
this.discoverySubject = null;
}
public get requestID(): string {
return this._requestID;
}
public get discoverRequestInfo(): DiscoverRequestInfo {
return this._discoverRequestInfo;
}
public discoveryObservable(): Observable<{ messageType: DiscoveryMessageType, params: any[] }> {
return this.discoverySubject.asObservable();
}
/**
* DiscoveryQueueing
*/
@RPCSubscriber({ method: 'DiscoveryService.Queueing' })
public DiscoveryQueueing(requestID: string, queueingDate: Date) {
this._requestID = requestID;
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryStatus({ discoveryStatus: DiscoveryStatusType.Queueing }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.Queueing, params: [requestID, queueingDate] });
}
/**
* DiscoveryQueueingFailed
*/
@RPCSubscriber({ method: 'DiscoveryService.QueueingFailed' })
public DiscoveryQueueingFailed(queueingFailedDate: Date) {
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryStatus({ discoveryStatus: DiscoveryStatusType.Stopped }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.QueueingFailed, params: [queueingFailedDate] });
this.destroy();
}
/**
* DiscoveryQueueingTimeout
*/
@RPCSubscriber({ method: 'DiscoveryService.QueueingTimeout' })
public DiscoveryQueueingTimeout(queueingTimeoutDate: Date) {
this._requestID = null;
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryStatus({ discoveryStatus: DiscoveryStatusType.Stopped }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.QueueingTimeout, params: [queueingTimeoutDate] });
this.destroy();
}
/**
* DiscoveryStart
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveryStart' })
public DiscoveryStart(startDate: Date) {
this.startDate = startDate;
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryStatus({ discoveryStatus: DiscoveryStatusType.Started }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveryStart, params: [startDate] });
}
/**
* DiscoveryStop
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveryStop' })
public DiscoveryStop(stopDate: Date) {
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryStatus({ discoveryStatus: DiscoveryStatusType.Stopped }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveryStop, params: [stopDate] });
this.destroy();
}
/**
* DiscoveryMode
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveryMode' })
public DiscoveryMode(discoveryMode: DiscoveryModeType) {
this.store.dispatch(new DiscoveryRequestStore.ChangeDiscoveryMode({ discoveryMode: discoveryMode }));
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveryMode, params: [discoveryMode] });
}
/**
* DiscoveryError
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveryError' })
public DiscoveryError(err: RPCError) {
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveryError, params: [err] });
}
/**
* DiscoveredHost
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveredHost' })
public DiscoveredHost(host: Host) {
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveredHost, params: [host] });
}
/**
* DiscoveredPort
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveredPort' })
public DiscoveredPort(port: Port) {
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveredPort, params: [port] });
}
/**
* DiscoveredService
*/
@RPCSubscriber({ method: 'DiscoveryService.DiscoveredService' })
public DiscoveredService(service: Service) {
this.discoverySubject.next({ messageType: DiscoveryMessageType.DiscoveredService, params: [service] });
}
}

View File

@ -0,0 +1,11 @@
import { Zone, Host, Port, DiscoverHost, DiscoverPort, DiscoverService } from '@overflow/model/discovery';
export interface DiscoverRequestInfo {
requesterID: string;
zone: Zone | null;
host: Host | null;
port: Port | null;
discoverHost: DiscoverHost | null;
discoverPort: DiscoverPort | null;
discoverService: DiscoverService | null;
}

View File

@ -0,0 +1,3 @@
export * from './link';
export * from './node';
export * from './discover-request-info';

View File

@ -0,0 +1,13 @@
export enum DiscoveryMessageType {
Queueing = 'Queueing',
QueueingFailed = 'QueueingFailed',
QueueingTimeout = 'QueueingTimeout',
DiscoveryStart = 'DiscoveryStart',
DiscoveryStop = 'DiscoveryStop',
DiscoveryMode = 'DiscoveryMode',
DiscoveryError = 'DiscoveryError',
DiscoveredHost = 'DiscoveredHost',
DiscoveredPort = 'DiscoveredPort',
DiscoveredService = 'DiscoveredService',
}

View File

@ -0,0 +1,7 @@
export enum DiscoveryStatusType {
Requested = 'Requested',
Queueing = 'Queueing',
Started = 'Started',
Stopping = 'Stopping',
Stopped = 'Stopped'
}

View File

@ -0,0 +1,3 @@
export * from './discovery-message';
export * from './discovery-status';
export * from './target-display';

View File

@ -0,0 +1,7 @@
export enum TargetDisplayType {
NONE = 'NONE',
MAP = 'MAP',
GRID = 'GRID',
TREE = 'TREE',
}

View File

@ -0,0 +1,48 @@
import { combineLatest, Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { DiscoverHost, DiscoverPort, DiscoverService, Zone, Host, Port } from '@overflow/model/discovery';
import * as AppStore from '../../store';
import { DiscoverRequestInfo } from '../model';
import { DiscoverySession } from '../discovery/discovery-session';
export class DiscoveryUtil {
/**
* name
*/
public static getRequestInfo(store: Store<any>): Observable<DiscoverRequestInfo> {
return combineLatest(
store.select(AppStore.EnvironmentSelector.UserSelector.selectMemberID),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectZone),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectHost),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectPort),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectDiscoverHost),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectDiscoverPort),
store.select(AppStore.DiscoverySelector.ConfigSelector.selectDiscoverService),
(
memberID: string,
zone: Zone, host: Host, port: Port,
discoverHost: DiscoverHost, discoverPort: DiscoverPort, discoverService: DiscoverService) => {
return {
requesterID: memberID,
zone,
host,
port,
discoverHost,
discoverPort,
discoverService,
};
}
);
}
public static getDiscoverySession(store: Store<any>): Observable<DiscoverySession> {
return combineLatest(
store.select(AppStore.DiscoverySelector.RequestSelector.selectDiscoverySession),
(discoverySession: DiscoverySession) => {
return discoverySession;
}
);
}
}

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomePageComponent } from './home-page.component';
const routes: Routes = [
{
path: '',
component: HomePageComponent,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomePageRoutingModule { }

View File

@ -1,6 +1,6 @@
<of-p-div #discoveryContainer>
<div *ngIf="showIntro" class="home-start">
<div class="start-button" (click)="discoveryConfigService.setDiscoveryStatus('Started')">
<of-p-div>
<div *ngIf="(showWelcomPage$ | async)" class="home-start">
<div class="start-button" (click)="requestDiscover()">
<svg version="1.1" viewBox="0 0 200 210">
<path d="M8.3,193.5c0.1,2.4,0.8,4.9,2.1,7.1c4.5,7.7,14.3,10.4,22.1,5.9l152.1-87.8c2-1.3,3.8-3.1,5.1-5.4
c4.5-7.7,1.8-17.6-5.9-22.1L31.6,3.4c-2.2-1.1-4.6-1.7-7.2-1.7c-8.9,0-16.2,7.2-16.2,16.2V193.5z" />
@ -10,127 +10,19 @@
<img src="assets/image/logo/overFlow_CI_white_90.png">
<h1>Network Scanner </h1>
<br>
<b>이용해 주셔서 감사합니다.</b>
<b>이용해 주셔서 감사합니다.</b>
<br> 좌측의 버튼을 클릭 하시면 기본 설정으로 스캐닝이 시작 됩니다. 설정의 변경을 원하시면 상단의 설정 영역을 클릭하여 변경이 가능합니다.
</div>
</div>
<div *ngIf="!showIntro" class="discovery-container">
<svg #discoveryTarget width="100%" height="100%">
<g>
<g *ngFor="let link of links">
<g class="link-container" [ngClass]="'link-'+link.target.group" [attr.sourceId]="link.source.id"
[attr.targetId]="link.target.id">
<line class="link" [attr.x1]="link.source.x" [attr.y1]="link.source.y" [attr.x2]="link.target.x" [attr.y2]="link.target.y"></line>
<text *ngIf="link.target.group === 'service'" class="linkTextClass" [attr.x]="(link.source.x - link.target.x) / 2 + link.target.x"
[attr.y]="(link.source.y - link.target.y) / 2 + link.target.y">{{link.target.target.port.portNumber}}</text>
</g>
</g>
<g *ngFor="let node of nodes">
<g class="node-container" [ngClass]="node.group" [attr.nodeId]="node.id" [attr.transform]="'translate(' + node.x + ',' + node.y + ')'">
<g *ngIf="node.group === 'zone'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
xlink:href="assets/image/node/icon/icon_overflow.svg"></image>
</g>
<g *ngIf="node.group === 'zone'">
<text class="textClass" [attr.y]="(node.r - 10) / 10 + 'em'" text-anchor="middle">{{node.target.network}}</text>
</g>
<g *ngIf="node.group !== 'zone'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
xlink:href="assets/image/node/icon/icon_background.svg"></image>
</g>
<g *ngIf="node.group === 'host'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
attr.xlink:href="assets/image/node/icon/icon_{{node.target.hostType | lowercase}}.svg"></image>
</g>
<g *ngIf="node.group === 'host' && node.target.hostType === 'HOST' && node.target.osType !== 'UNKNOWN'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
attr.xlink:href="assets/image/node/logo/logo_{{node.target.osType | lowercase}}.svg"></image>
</g>
<!-- <g *ngIf="node.group === 'host' && node.target.hostType !== 'HOST' && node.target.hostVendor !== ''">
<text class="textClass" [attr.y]="(node.r - 30) / 10 + 'em'" text-anchor="middle">{{node.target.hostVendor}}</text>
</g> -->
<g *ngIf="node.group === 'host'">
<text class="textClass" [attr.y]="(node.r - 10) / 10 + 'em'" text-anchor="middle">{{node.target.address}}</text>
</g>
<g *ngIf="node.group === 'service'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
attr.xlink:href="assets/image/node/icon/icon_{{node.target.serviceType | lowercase}}.svg"></image>
</g>
<g *ngIf="node.group === 'service' && node.target.key !== 'UNKNOWN'">
<image [attr.x]="-node.r" [attr.y]="-node.r" [attr.width]="node.r * 2" [attr.height]="node.r * 2"
attr.xlink:href="assets/image/node/logo/logo_{{node.target.key | lowercase}}.svg"></image>
</g>
<g *ngIf="node.group === 'service'">
<text class="textClass" [attr.y]="(node.r - 10) / 10 + 'em'" text-anchor="middle">{{node.target.name}}</text>
</g>
</g>
</g>
</g>
</svg>
<p-card class="ui-map-info" *ngIf="discoveryResult">
<div class="ui-map-info-row ui-border-bottom">
<div>Total Hosts: </div>{{discoveryResult.totalHosts}}
</div>
<div class="ui-map-info-row ui-border-bottom">
<div>Total Services: </div>{{discoveryResult.totalServices}}
</div>
<div class="ui-map-info-row">
<div>Elapsed: </div>{{discoveryResult.elapsedTime}}
</div>
</p-card>
<div #detailSidebar style="position: sticky; top: 0; left: 0; height: 100%;"></div>
<p-sidebar [(visible)]="displaySidebar" [modal]="false" [appendTo]="detailSidebar" styleClass="ui-sidebar-md"
position="right" (onHide)="onHideDetail()" [style]="{position:'absolute'}">
<div *ngIf="selectedNode !== null">
<app-node-detail [node]="selectedNode" (otherHostSelect)="otherHostSelected($event)"></app-node-detail>
</div>
</p-sidebar>
<div *ngIf="!(showWelcomPage$ | async)" class="display-container">
<app-target-display></app-target-display>
</div>
</of-p-div>
<p-confirmDialog></p-confirmDialog>
<p-blockUI id="discovery-blockUI" [target]="discoveryContainer" [blocked]="discoveryRequestID !== null">
</p-blockUI>
<p-dialog [showHeader]="false" [(visible)]="stopping" [modal]="true" [responsive]="false" [width]="300" [height]="100"
[minY]="70" [closeOnEscape]="false" [closable]="false">
<div class="ui-dialog-text-center">Discovery is being stopped...</div>
</p-dialog>
<!-- <div class="ui-fluid">
<div class="ui-g">
<div class="ui-g-12">
<h1>Home works!!!!</h1>
<p-panel #content [showHeader]="false" class="block-panel">
<div class="ui-g" dir="rtl">
<button class="ui-button-width-fit" type="button" label="Discovery" icon="ui-icon-search" pButton></button>
</div>
</p-panel>
<h3 class="first">Document</h3>
<p-blockUI [blocked]="blockedDocument"></p-blockUI>
<button type="button" pButton label="Block" (click)="blockDocument()"></button>
<h3>Panel</h3>
<button type="button" pButton label="Block" (click)="blockedPanel=true"></button>
<button type="button" pButton label="Unblock" (click)="blockedPanel=false"></button>
<p-blockUI [target]="pnl" [blocked]="blockedPanel">
<i class="fa fa-lock fa-5x" style="position:absolute;top:25%;left:50%"></i>
</p-blockUI>
<p-panel #pnl header="Godfather I" [style]="{'margin-top':'20px'}">
The story begins as Don Vito Corleone, the head of a New York Mafia family, oversees his daughter's wedding. His beloved
son Michael has just come home from the war, but does not intend to become part of his father's business.
Through Michael's life the nature of the family business becomes clear. The business of the family is
just like the head of the family, kind and benevolent to those who give respect, but given to ruthless
violence whenever anything stands against the good of the family.
</p-panel>
</div>
</div>
</div> -->

View File

@ -48,105 +48,15 @@
padding: .6em .75em;
}
.discovery-container {
height: 100vh;
margin: -0.6em -0.9em -0.7em -0.9em; //-0.5em -0.75em;
padding: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
min-width: 400px; //text-align: center;
}
.link {
stroke: #999;
stroke-opacity: 0.6;
}
.textClass {
stroke: #323232;
font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
font-weight: normal;
stroke-width: .5;
font-size: 14px;
}
.linkTextClass {
stroke: #b6b4b4;
font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
font-weight: normal;
stroke-width: .3;
font-size: 9px;
}
.focused {
opacity: 1 !important;
}
.semi-focused {
opacity: 0.8 !important;
}
.semi-unfocused {
opacity: 0.8;
}
.unfocused {
opacity: 0.3;
}
.semi-unselected {
opacity: 0.8;
}
.unselected {
opacity: 0.3;
}
.ui-map-info {
position: absolute;
top: 10px;
left: 10px;
.ui-map-info-row {
//display: inline;
font-size: 0.8em;
line-height: 2em;
width: 120px;
font-weight: bold;
user-select: text;
div {
display: inline-block;
font-weight: normal;
padding-left: 5px;
width: 75px;
user-select: text;
}
}
.ui-border-bottom {
border-bottom: 1px solid #d6d6d6;
}
}
/deep/ .ui-card-body {
padding: 0.5em;
border: 1px solid #d6d6d6;
border-radius: 3px;
}
.detail-sidebar {
top: 0;
left: 0;
height: 100%;
}
// /deep/ .ui-blockui {
// opacity: 0.1;
// }
/deep/ .ui-widget-overlay {
background-color: #666666;
opacity: .20;
filter: Alpha(Opacity=50);
.display-container {
position: relative; // height: 100vh;
// margin: -0.6em -0.9em -0.7em -0.9em; //-0.5em -0.75em;
// padding: 0;
// box-sizing: border-box;
// display: flex;
// align-items: center;
// justify-content: center;
// min-width: 400px; //text-align: center;
}
.ui-dialog-text-center {

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CommonsUIModule } from '@overflow/commons/ui/commons-ui.module';
import { HomePageComponent } from './home-page.component';
import { HomePageRoutingModule } from './home-page-routing.module';
import { CommonsModule } from '../../../commons/commons.module';
@NgModule({
imports: [
CommonModule,
CommonsUIModule,
HomePageRoutingModule,
CommonsModule
],
entryComponents: [
],
declarations: [HomePageComponent],
})
export class HomePageModule { }

View File

@ -0,0 +1,5 @@
import { HomePageComponent } from './home-page.component';
export const PAGES = [
HomePageComponent,
];

8
src/app/pages/index.ts Normal file
View File

@ -0,0 +1,8 @@
import { PagesComponent } from './pages.component';
import { PAGES as HOME_PAGES } from './home';
export const PAGES = [
PagesComponent,
...HOME_PAGES,
];

View File

@ -1,20 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PagesComponent } from './pages.component';
const routes: Routes = [
{
path: '',
component: PagesComponent,
children: [
{ path: '', redirectTo: 'home' },
{ path: 'home', loadChildren: './home/home-page.module#HomePageModule' },
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PagesRoutingModule { }

View File

@ -1,9 +1,9 @@
<app-toolbar>
<app-nic-dropdown [blockTarget]="pagesContent" (select)="onNICSelect($event)"></app-nic-dropdown>
<app-scanner-setting-dropdown [blockTarget]="pagesContent" (ready)="onConfigReady($event)"></app-scanner-setting-dropdown>
<app-layout-toolbar-nic-dropdown [blockTarget]="pagesContent"></app-layout-toolbar-nic-dropdown>
<app-layout-toolbar-scanner-setting-dropdown [blockTarget]="pagesContent"></app-layout-toolbar-scanner-setting-dropdown>
<ng-template #play>
<div class="toolbar-button push-pull-button" style="width: 32px">
<button class="button-component" type="button" (click)="onStart()" [hidden]="discoveryStatus === 'Started' ">
<button class="button-component" type="button" (click)="requestDiscover()" [hidden]="(discoveryStatus$ | async) === 'Started' ">
<svg aria-hidden="true" class="octicon icon" version="1.1" viewBox="0 0 15 17">
<path d="M0.2,15.6c0,0.2,0.1,0.4,0.2,0.6c0.4,0.6,1.2,0.8,1.8,0.5l12.2-7.1c0.2-0.1,0.3-0.3,0.4-0.4
c0.4-0.6,0.1-1.4-0.5-1.8L2.1,0.3C1.9,0.2,1.7,0.2,1.5,0.2c-0.7,0-1.3,0.6-1.3,1.3V15.6z"></path>
@ -13,8 +13,8 @@
</ng-template>
<!-- Stop button -->
<div class="toolbar-button push-pull-button" style="width: 36px" *ngIf="discoveryStatus === 'Started' else play">
<button class="button-component" type="button" (click)="onStop()">
<div class="toolbar-button push-pull-button" style="width: 36px" *ngIf="(discoveryStatus$ | async) === 'Started' else play">
<button class="button-component" type="button" (click)="requestDiscoverStop()">
<svg aria-hidden="true" class="octicon icon" version="1.1" viewBox="0 0 15 17">
<path d="M13.6,16.1H1.4c-0.8,0-1.4-0.6-1.4-1.4V2.5c0-0.8,0.6-1.4,1.4-1.4h12.2c0.8,0,1.4,0.6,1.4,1.4v12.2
C15,15.5,14.4,16.1,13.6,16.1z" />
@ -23,7 +23,7 @@
</div>
<!-- Loading image -->
<div class="toolbar-button push-pull-button" style="width: 36px" *ngIf="discoveryStatus !== 'Stopped' ">
<div class="toolbar-button push-pull-button" style="width: 36px" *ngIf="(discoveryStatus$ | async) !== 'Stopped' ">
<button class="button-component" type="button">
<svg aria-hidden="true" class="octicon icon spin" version="1.1" viewBox="0 0 15 15">
<path d="M14.6,5.2l-0.7,0.3l-0.7,0.3c0.7,2.6-0.3,5.4-2.7,6.8c-2.3,1.3-5.1,1-7-0.7L4.4,12l0.1-1.5l-3.1-0.2l-0.9,3.2
@ -33,8 +33,8 @@
</svg>
</button>
</div>
<div *ngIf="discoveryMode" class="ui-menu-mode">
<span>{{discoveryMode}} Mode</span>
<div *ngIf="(discoveryMode$ | async)" class="ui-menu-mode">
<span>{{discoveryMode$ | async}} Mode</span>
</div>
</app-toolbar>
@ -43,3 +43,6 @@
<router-outlet #o="outlet"></router-outlet>
</div>
</of-p-div>
<p-blockUI id="pagesContent-blockUI" [target]="pagesContent" [blocked]="blockPagesContent$ | async">
</p-blockUI>

View File

@ -2,8 +2,17 @@ import {
Component, AfterViewInit, ElementRef, Renderer, ViewChild, OnDestroy, OnInit, NgZone
} from '@angular/core';
import { Router } from '@angular/router';
import { DiscoverHost, Zone } from '@overflow/model/discovery';
import { DiscoveryConfigService, DiscoveryStatusType } from '../../commons/service/discovery-config.service';
import { Store, select } from '@ngrx/store';
import { DiscoveryStatusType } from '../core/type';
import * as AppStore from '../store';
import * as DiscoveryRequestStore from '../store/discovery/request';
import { DiscoveryUtil } from '../core/util/discovery.util';
import { map, catchError, take } from 'rxjs/operators';
import { of } from 'rxjs';
import { ProbeService } from '../service/probe.service';
import { DiscoverySession } from '../core/discovery/discovery-session';
enum MenuOrientation {
STATIC,
@ -57,17 +66,18 @@ export class PagesComponent implements AfterViewInit, OnDestroy, OnInit {
rippleInitListener: any;
rippleMouseDownListener: any;
discoveryMode: string;
discoveryStatus: DiscoveryStatusType;
discoveryMode$ = this.store.pipe(select(AppStore.DiscoverySelector.RequestSelector.selectDiscoveryMode));
discoveryStatus$ = this.store.pipe(select(AppStore.DiscoverySelector.RequestSelector.selectDiscoveryStatus));
blockPagesContent$ = this.store.pipe(select(AppStore.UISelector.LayoutSelector.selectBlockPagesContent));
constructor(
public renderer: Renderer,
private store: Store<any>,
public zone: NgZone,
private router: Router,
private discoveryConfigService: DiscoveryConfigService,
private probeService: ProbeService,
) {
}
@ -76,14 +86,6 @@ export class PagesComponent implements AfterViewInit, OnDestroy, OnInit {
return;
}
this.zone.runOutsideAngular(() => { this.bindRipple(); });
this.discoveryConfigService.discoveryMode.subscribe(res => {
this.discoveryMode = res as string;
});
this.discoveryConfigService.discoveryStatus.subscribe(res => {
this.discoveryStatus = res as DiscoveryStatusType;
});
}
bindRipple() {
@ -365,20 +367,12 @@ export class PagesComponent implements AfterViewInit, OnDestroy, OnInit {
this.router.navigate([command]);
}
onNICSelect(zone: Zone) {
this.discoveryConfigService.setZone(zone);
requestDiscover() {
DiscoverySession.requestDiscover(this.store, this.probeService);
}
onConfigReady(discoverHost: DiscoverHost) {
this.discoveryConfigService.setDiscoverHost(discoverHost);
}
onStart() {
this.discoveryConfigService.setDiscoveryStatus(DiscoveryStatusType.Started);
}
onStop() {
this.discoveryConfigService.setDiscoveryStatus(DiscoveryStatusType.Stopping);
requestDiscoverStop() {
DiscoverySession.requestDiscoverStop(this.store);
}
}

View File

@ -1,28 +0,0 @@
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { CommonModule, LocationStrategy, HashLocationStrategy } from '@angular/common';
import { CommonsUIModule } from '@overflow/commons/ui/commons-ui.module';
import { CommonsModule } from '../../commons/commons.module';
import { PagesComponent } from './pages.component';
import { PagesRoutingModule } from './pages-routing.module';
import { DiscoveryConfigService } from '../../commons/service/discovery-config.service';
@NgModule({
imports: [
CommonModule,
PagesRoutingModule,
CommonsUIModule,
CommonsModule,
],
declarations: [
PagesComponent,
],
providers: [
{ provide: LocationStrategy, useClass: HashLocationStrategy },
DiscoveryConfigService,
]
})
export class PagesModule { }

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import Dexie from 'dexie';
import { LaunchState } from '../model';
import { LaunchState } from '../../commons/model';
const DatabaseVersion = 1;

View File

@ -0,0 +1,55 @@
import { Injectable } from '@angular/core';
import { Zone, DiscoverHost, Host, DiscoverPort, Port, DiscoverService } from '@overflow/model/discovery';
import { ProbeService } from './probe.service';
import { DiscoverRequestInfo } from '../core/model';
@Injectable()
export class DiscoveryService {
public constructor(
private probeService: ProbeService,
) {
}
public discover(discoverRequestInfo: DiscoverRequestInfo): void {
if (null !== discoverRequestInfo.zone && null !== discoverRequestInfo.discoverHost) {
this.discoverHost(
discoverRequestInfo.requesterID,
discoverRequestInfo.zone,
discoverRequestInfo.discoverHost
);
} else if (null !== discoverRequestInfo.host && null !== discoverRequestInfo.discoverPort) {
this.discoverPort(
discoverRequestInfo.requesterID,
discoverRequestInfo.host,
discoverRequestInfo.discoverPort
);
} else if (null !== discoverRequestInfo.port && null !== discoverRequestInfo.discoverService) {
this.discoverService(
discoverRequestInfo.requesterID,
discoverRequestInfo.port,
discoverRequestInfo.discoverService
);
} else {
return;
}
}
public discoverHost(requesterID: string, zone: Zone, discoverHost: DiscoverHost): void {
this.probeService.send('DiscoveryService.DiscoverHost', requesterID, zone, discoverHost);
}
public discoverPort(requesterID: string, host: Host, discoverPort: DiscoverPort): void {
this.probeService.send('DiscoveryService.DiscoverPort', requesterID, host, discoverPort);
}
public discoverService(requesterID: string, port: Port, discoverService: DiscoverService): void {
this.probeService.send('DiscoveryService.DiscoverService', requesterID, port, discoverService);
}
public discoverStop(requesterID: string, requestID: string): void {
this.probeService.send('DiscoveryService.DiscoverStop', requesterID, requestID);
}
}

View File

@ -4,9 +4,9 @@ import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { ipcRenderer } from 'electron';
import { MenuEvent } from '../type';
import { LaunchState } from '../model';
import { MenuEvent } from '../../commons/type';
import { LaunchService } from './launch.service';
import { LaunchState } from '../../commons/model';
@Injectable({
providedIn: 'root'

View File

@ -2,12 +2,16 @@ import { DatabaseService } from './database.service';
import { ElectronProxyService } from './electron-proxy.service';
import { LaunchService } from './launch.service';
import { ProbeService } from './probe.service';
import { DiscoveryConfigService } from './discovery-config.service';
import { MachineService } from './machine.service';
import { DiscoveryService } from './discovery.service';
import { PingService } from './ping.service';
export const SERVICES = [
DatabaseService,
ElectronProxyService,
LaunchService,
ProbeService,
DiscoveryConfigService
MachineService,
DiscoveryService,
PingService,
];

View File

@ -2,13 +2,13 @@ import { Injectable } from '@angular/core';
import { Observable, defer } from 'rxjs';
import { DatabaseService } from './database.service';
import { LaunchState } from '../model';
import { LaunchState } from '../../commons/model';
@Injectable({
providedIn: 'root'
})
export class LaunchService {
public constructor(private readonly databaseService: DatabaseService) {}
public constructor(private readonly databaseService: DatabaseService) { }
public save(launchState: LaunchState): Observable<number> {
return defer(async () => {

View File

@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Interface } from '@overflow/model/net/nic';
import { ProbeService } from './probe.service';
@Injectable()
export class MachineService {
public constructor(
private probeService: ProbeService,
) {
}
public interfaces(): Observable<Interface[]> {
return this.probeService.call<Interface[]>('MachineService.Interfaces');
}
}

View File

@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Interface } from '@overflow/model/net/nic';
import { ProbeService } from './probe.service';
import { PingResult } from '@overflow/model/ping';
import { Host, Service } from '@overflow/model/discovery';
import { PingOption } from '@overflow/model/config/ping';
@Injectable()
export class PingService {
public constructor(
private probeService: ProbeService,
) {
}
public pingHost(host: Host, pingOption: PingOption): Observable<PingResult> {
return this.probeService.call<PingResult>('PingService.PingHost', host, pingOption);
}
public pingService(service: Service, pingOption: PingOption): Observable<PingResult> {
return this.probeService.call<PingResult>('PingService.PingService', service, pingOption);
}
}

View File

@ -0,0 +1,39 @@
import { Action } from '@ngrx/store';
export enum ActionType {
AppInit = '[app] AppInit',
AppAfterContentInit = '[app] AppAfterContentInit',
AppAfterViewInit = '[app] AppAfterViewInit',
AppDestroy = '[app] AppDestroy',
}
export class AppInit implements Action {
readonly type = ActionType.AppInit;
constructor() { }
}
export class AppAfterContentInit implements Action {
readonly type = ActionType.AppAfterContentInit;
constructor() { }
}
export class AppAfterViewInit implements Action {
readonly type = ActionType.AppAfterViewInit;
constructor() { }
}
export class AppDestroy implements Action {
readonly type = ActionType.AppDestroy;
constructor() { }
}
export type Actions =
| AppInit
| AppAfterContentInit
| AppAfterViewInit
| AppDestroy
;

View File

@ -0,0 +1 @@
export * from './app.action';

View File

@ -0,0 +1,26 @@
import { Action } from '@ngrx/store';
import { Zone, DiscoverHost } from '@overflow/model/discovery';
export enum ActionType {
ChangeZone = '[app.discovery] ChangeZone',
ChangeDiscoverHost = '[app.discovery] ChangeDiscoverHost',
}
export class ChangeZone implements Action {
readonly type = ActionType.ChangeZone;
constructor(public payload: Zone) { }
}
export class ChangeDiscoverHost implements Action {
readonly type = ActionType.ChangeDiscoverHost;
constructor(public payload: DiscoverHost) { }
}
export type Actions =
| ChangeZone
| ChangeDiscoverHost
;

View File

@ -0,0 +1,8 @@
import { Injectable } from '@angular/core';
@Injectable()
export class ConfigEffects {
constructor(
) { }
}

View File

@ -0,0 +1,31 @@
import {
Actions,
ActionType,
} from './config.action';
import {
State,
initialState,
} from './config.state';
export function reducer(state: State = initialState, action: Actions): State {
switch (action.type) {
case ActionType.ChangeZone: {
return {
...state,
zone: action.payload,
};
}
case ActionType.ChangeDiscoverHost: {
return {
...state,
discoverHost: action.payload,
};
}
default: {
return state;
}
}
}

View File

@ -0,0 +1,32 @@
import { Selector, createSelector } from '@ngrx/store';
import { Zone, DiscoverHost, Port, Host, DiscoverPort, DiscoverService } from '@overflow/model/discovery';
export interface State {
zone: Zone | null;
host: Host | null;
port: Port | null;
discoverHost: DiscoverHost | null;
discoverPort: DiscoverPort | null;
discoverService: DiscoverService | null;
}
export const initialState: State = {
zone: null,
host: null,
port: null,
discoverHost: null,
discoverPort: null,
discoverService: null,
};
export function getSelectors<S>(selector: Selector<any, State>) {
return {
selectZone: createSelector(selector, (state: State) => state.zone),
selectHost: createSelector(selector, (state: State) => state.host),
selectPort: createSelector(selector, (state: State) => state.port),
selectDiscoverHost: createSelector(selector, (state: State) => state.discoverHost),
selectDiscoverPort: createSelector(selector, (state: State) => state.discoverPort),
selectDiscoverService: createSelector(selector, (state: State) => state.discoverService),
};
}

View File

@ -0,0 +1,4 @@
export * from './config.action';
export * from './config.effect';
export * from './config.reducer';
export * from './config.state';

View File

@ -0,0 +1,38 @@
import { Type } from '@angular/core';
import {
Selector,
createSelector,
combineReducers
} from '@ngrx/store';
import * as ConfigStore from './config';
import * as RequestStore from './request';
export interface State {
config: ConfigStore.State;
request: RequestStore.State;
}
export const EFFECTS: Type<any>[] = [
ConfigStore.ConfigEffects,
RequestStore.RequestEffects,
];
export const REDUCER = combineReducers({
config: ConfigStore.reducer,
request: RequestStore.reducer,
});
export function getSelectors<S>(selector: Selector<any, State>) {
return {
ConfigSelector: ConfigStore.getSelectors(createSelector(
selector,
(state: State) => state.config,
)),
RequestSelector: RequestStore.getSelectors(createSelector(
selector,
(state: State) => state.request,
)),
};
}

View File

@ -0,0 +1,4 @@
export * from './request.action';
export * from './request.effect';
export * from './request.reducer';
export * from './request.state';

View File

@ -0,0 +1,54 @@
import { Action } from '@ngrx/store';
import { DiscoveryModeType } from '@overflow/model/discovery';
import { DiscoverySession } from '../../../core/discovery/discovery-session';
import { DiscoveryStatusType } from 'src/app/core/type';
export enum ActionType {
RequestDiscover = '[app.discovery] RequestDiscover',
RequestDiscoverStop = '[app.discovery] RequestDiscoverStop',
ChangeDiscoveryMode = '[app.discovery] ChangeDiscoveryMode',
ChangeDiscoveryStatus = '[app.discovery] ChangeDiscoveryStatus',
DiscoveryComplete = '[app.discovery] DiscoveryComplete',
}
export class RequestDiscover implements Action {
readonly type = ActionType.RequestDiscover;
constructor(public payload: { discoverySession: DiscoverySession }) { }
}
export class ChangeDiscoveryMode implements Action {
readonly type = ActionType.ChangeDiscoveryMode;
constructor(public payload: { discoveryMode: DiscoveryModeType }) { }
}
export class RequestDiscoverStop implements Action {
readonly type = ActionType.RequestDiscoverStop;
constructor(public payload: { discoverySession: DiscoverySession }) { }
}
export class ChangeDiscoveryStatus implements Action {
readonly type = ActionType.ChangeDiscoveryStatus;
constructor(public payload: { discoveryStatus: DiscoveryStatusType }) { }
}
export class DiscoveryComplete implements Action {
readonly type = ActionType.DiscoveryComplete;
constructor() { }
}
export type Actions =
| RequestDiscover
| ChangeDiscoveryMode
| RequestDiscoverStop
| ChangeDiscoveryStatus
| DiscoveryComplete
;

View File

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { map, tap } from 'rxjs/operators';
import {
ActionType,
RequestDiscover,
RequestDiscoverStop,
ChangeDiscoveryStatus,
} from './request.action';
import { DiscoveryService } from '../../../service/discovery.service';
import { DiscoveryStatusType } from 'src/app/core/type';
@Injectable()
export class RequestEffects {
@Effect({ dispatch: false })
requestDiscover$ = this.actions$.pipe(
ofType<RequestDiscover>(ActionType.RequestDiscover),
map((action) => {
this.discoveryService.discover(action.payload.discoverySession.discoverRequestInfo);
}),
);
@Effect({ dispatch: false })
requestDiscoverStop$ = this.actions$.pipe(
ofType<RequestDiscoverStop>(ActionType.RequestDiscoverStop),
map((action) => action.payload),
map((payload) => {
this.discoveryService.discoverStop(payload.discoverySession.discoverRequestInfo.requesterID, payload.discoverySession.requestID);
})
);
constructor(
private store: Store<any>,
private actions$: Actions,
private router: Router,
private discoveryService: DiscoveryService,
) { }
}

View File

@ -0,0 +1,54 @@
import {
Actions,
ActionType,
} from './request.action';
import {
State,
initialState,
} from './request.state';
import { DiscoveryStatusType } from '../../../core/type';
export function reducer(state: State = initialState, action: Actions): State {
switch (action.type) {
case ActionType.RequestDiscover: {
return {
...state,
discoverySession: action.payload.discoverySession,
discoveryStatus: DiscoveryStatusType.Requested,
};
}
case ActionType.RequestDiscoverStop: {
return {
...state,
discoveryStatus: DiscoveryStatusType.Stopping,
};
}
case ActionType.ChangeDiscoveryMode: {
return {
...state,
discoveryMode: action.payload.discoveryMode,
};
}
case ActionType.ChangeDiscoveryStatus: {
return {
...state,
discoveryStatus: action.payload.discoveryStatus,
};
}
case ActionType.DiscoveryComplete: {
return {
...state,
discoverySession: null,
};
}
default: {
return state;
}
}
}

View File

@ -0,0 +1,27 @@
import { Selector, createSelector } from '@ngrx/store';
import { DiscoveryModeType } from '@overflow/model/discovery';
import { DiscoveryStatusType } from '../../../core/type';
import { DiscoverySession } from '../../../core/discovery/discovery-session';
export interface State {
discoveryMode: DiscoveryModeType | null;
discoveryStatus: DiscoveryStatusType | null;
discoverySession: DiscoverySession | null;
}
export const initialState: State = {
discoveryMode: null,
discoveryStatus: DiscoveryStatusType.Stopped,
discoverySession: null,
};
export function getSelectors<S>(selector: Selector<any, State>) {
return {
selectDiscoveryMode: createSelector(selector, (state: State) => state.discoveryMode),
selectDiscoveryStatus: createSelector(selector, (state: State) => state.discoveryStatus),
selectDiscoverySession: createSelector(selector, (state: State) => state.discoverySession),
};
}

View File

@ -0,0 +1,38 @@
import { Type } from '@angular/core';
import {
Selector,
createSelector,
combineReducers
} from '@ngrx/store';
import * as InterfacesStore from './interfaces';
import * as UserStore from './user';
export interface State {
interfaces: InterfacesStore.State;
user: UserStore.State;
}
export const EFFECTS: Type<any>[] = [
InterfacesStore.InterfacesEffects,
UserStore.UserEffects,
];
export const REDUCER = combineReducers({
interfaces: InterfacesStore.reducer,
user: UserStore.reducer,
});
export function getSelectors<S>(selector: Selector<any, State>) {
return {
InterfacesSelector: InterfacesStore.getSelectors(createSelector(
selector,
(state: State) => state.interfaces,
)),
UserSelector: UserStore.getSelectors(createSelector(
selector,
(state: State) => state.user,
)),
};
}

View File

@ -0,0 +1,4 @@
export * from './interfaces.action';
export * from './interfaces.effect';
export * from './interfaces.reducer';
export * from './interfaces.state';

Some files were not shown because too many files have changed in this diff Show More