+ [(ngModel)]="filterServices[service.name]">
diff --git a/@overflow/discovery/component/search-result.component.ts b/@overflow/discovery/component/search-result.component.ts
index 9fbcdd9..ce80791 100644
--- a/@overflow/discovery/component/search-result.component.ts
+++ b/@overflow/discovery/component/search-result.component.ts
@@ -1,8 +1,6 @@
import {
Component, Input,
- SimpleChanges,
OnInit,
- OnChanges,
} from '@angular/core';
import { Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
import { TreeNode, Message, Tree } from 'primeng/primeng';
@@ -23,12 +21,11 @@ import { catchError, map, tap, take } from 'rxjs/operators';
InfraRegistService
]
})
-export class SearchResultComponent implements OnInit, OnChanges {
+export class SearchResultComponent implements OnInit {
@Input() probeHost: ProbeHost;
@Input() filterWord: string;
@Input() filterServices: Map
;
- @Input() finished: boolean;
infras: Infra[];
existTargets: Target[];
@@ -62,7 +59,16 @@ export class SearchResultComponent implements OnInit, OnChanges {
children: this.hostNode,
expanded: true
});
- this.getExistTarget();
+ // this.getExistTarget();
+ }
+
+ discoveryStarted(startedAt: Date) {
+ console.log('Discovery Started at: ' + startedAt);
+ }
+
+ discoveryStopped(stoppedAt: Date) {
+ console.log('Discovery Stopped at: ' + stoppedAt);
+ this.saveDiscoveredInfras();
}
getExistTarget() {
@@ -89,11 +95,11 @@ export class SearchResultComponent implements OnInit, OnChanges {
).subscribe();
}
- ngOnChanges(changes: SimpleChanges): void {
- if (changes['finished'] && changes['finished'].currentValue === true) {
- this.saveDiscoveredInfras();
- }
- }
+ // ngOnChanges(changes: SimpleChanges): void {
+ // if (changes['finished'] && changes['finished'].currentValue === true) {
+ // this.saveDiscoveredInfras();
+ // }
+ // }
displayInform() {
this.msgs = [];
@@ -107,9 +113,9 @@ export class SearchResultComponent implements OnInit, OnChanges {
this.hostNode = [];
for (const infra of this.infras) {
switch (infra.metaInfraType) {
- case toMetaInfraType(MetaInfraTypeEnum.ZONE):
- const infraZone: InfraZone = infra;
- break;
+ // case toMetaInfraType(MetaInfraTypeEnum.ZONE):
+ // const infraZone: InfraZone = infra;
+ // break;
case toMetaInfraType(MetaInfraTypeEnum.HOST):
const infraHost: InfraHost = infra;
this.addInfraHost(infraHost);
diff --git a/@overflow/discovery/component2/animation.ts b/@overflow/discovery/component2/animation.ts
new file mode 100644
index 0000000..6d8f3ca
--- /dev/null
+++ b/@overflow/discovery/component2/animation.ts
@@ -0,0 +1,35 @@
+import { trigger, state, transition, style, animate, query, stagger, keyframes } from '@angular/animations';
+
+export const Anim = [
+ trigger('discoveryFilterAnim', [
+ transition('void => *', [
+ query('*', style({ opacity: 0 }), { optional: true }),
+ query('*', stagger('3ms', [
+ animate('0.08s ease-in', keyframes([
+ style({ opacity: 0, transform: 'translateY(-75%)', offset: 0 }),
+ style({ opacity: .5, transform: 'translateY(35px)', offset: 0.3 }),
+ style({ opacity: 1, transform: 'translateY(0)', offset: 1.0 }),
+ ]))]), { optional: true }),
+ ]),
+ // transition('* => void', [
+ // query('*', style({ opacity: 1 }), { optional: true }),
+ // query('*', stagger('5ms', [
+ // animate('0.08s ease-in', keyframes([
+ // style({ opacity: 1, transform: 'translateY(0)', offset: 0 }),
+ // style({ opacity: .5, transform: 'translateY(35px)', offset: 0.3 }),
+ // style({ opacity: 0, transform: 'translateY(-75%)', offset: 1.0 }),
+ // ]))]), { optional: true }),
+ // ])
+ ]),
+ trigger('discoveryResultAnim', [
+ transition('void => *', [
+ query('*', style({ opacity: 0 }), { optional: true }),
+ query('*', stagger('30ms', [
+ animate('0.08s ease-in', keyframes([
+ style({ opacity: 0, transform: 'translateX(-95%)', offset: 0 }),
+ style({ opacity: .5, transform: 'translateX(95px)', offset: 0.3 }),
+ style({ opacity: 1, transform: 'translateX(0)', offset: 1.0 }),
+ ]))]), { optional: true }),
+ ]),
+ ]),
+];
diff --git a/@overflow/discovery/component2/discovery.component.html b/@overflow/discovery/component2/discovery.component.html
new file mode 100644
index 0000000..5902ffc
--- /dev/null
+++ b/@overflow/discovery/component2/discovery.component.html
@@ -0,0 +1,34 @@
+Discovery
+
+
+
+
+
+ Choose a Probe to perform Discovery.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Network Discovery 설명 페이지
+
+
+
\ No newline at end of file
diff --git a/@overflow/discovery/component2/discovery.component.ts b/@overflow/discovery/component2/discovery.component.ts
new file mode 100644
index 0000000..87e2f5c
--- /dev/null
+++ b/@overflow/discovery/component2/discovery.component.ts
@@ -0,0 +1,121 @@
+import {
+ Component, Input, OnDestroy, ViewChild,
+} from '@angular/core';
+
+
+import { Observable, of, Subscription } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+
+import { ProbeHost } from '@overflow/commons-typescript/model/probe';
+import { DiscoverZone, Zone, Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
+
+import { Anim } from './animation';
+import { DiscoveryService } from '../service/discovery.service';
+import { DiscoverySubscriber, DiscoveryNotify } from '../subscriber/discovery.subscriber';
+import { SearchResultComponent } from './search-result.component';
+import { SearchFilterComponent } from './search-filter.component';
+
+@Component({
+ selector: 'of-discovery',
+ templateUrl: './discovery.component.html',
+ animations: Anim,
+})
+export class DiscoveryComponent implements OnDestroy {
+
+ @Input() probeHostID;
+ pending$: Observable;
+ error$: Observable;
+ discoverySubscription: Subscription;
+
+ selectedProbe: ProbeHost;
+ requested: boolean;
+ discoverZone: DiscoverZone;
+
+ filterWord: string;
+ filterServices: Service[];
+
+ startDate: Date;
+ stopDate: Date;
+
+ @ViewChild('discoveryResult') discoveryResult: SearchResultComponent;
+ @ViewChild('discoveryFilter') discoveryFilter: SearchFilterComponent;
+
+ constructor(
+ private discoveryService: DiscoveryService,
+ private discoverySubscriber: DiscoverySubscriber,
+ ) {
+ this.discoverySubscription = null;
+ }
+
+ ngOnDestroy(): void {
+ if (null !== this.discoverySubscription) {
+ this.discoverySubscription.unsubscribe();
+ }
+ }
+
+ onRequestDiscovery(dz: DiscoverZone) {
+ this.requested = true;
+ this.discoverZone = dz;
+
+ const zone: Zone = {
+ network: this.selectedProbe.infraHost.infraZone.network, // this.selectedProbe.probe.cidr
+ address: this.selectedProbe.infraHost.infraZone.address.split('/')[0],
+ iface: this.selectedProbe.infraHost.infraZone.iface,
+ mac: this.selectedProbe.infraHost.infraZone.mac,
+ metaIPType: this.selectedProbe.infraHost.infraZone.metaIPType,
+ };
+
+ this.discoveryService.discoverHost(this.selectedProbe.probe.probeKey, zone, dz.discoverHost);
+
+ this.discoverySubscription = this.discoverySubscriber.observable().pipe(
+ map((discoveryNotify: DiscoveryNotify) => {
+ switch (discoveryNotify.method) {
+ case 'DiscoveryService.discoveryStart': {
+ const startDate = discoveryNotify.params as Date;
+ this.discoveryResult.discoveryStarted(startDate);
+ break;
+ }
+ case 'DiscoveryService.discoveryStop': {
+ const stopDate = discoveryNotify.params as Date;
+ this.discoveryResult.discoveryStopped(stopDate);
+ this.discoverySubscription.unsubscribe();
+ this.discoverySubscription = null;
+ break;
+ }
+ case 'DiscoveryService.discoveredZone': {
+ const _zone = discoveryNotify.params as Zone;
+ break;
+ }
+ case 'DiscoveryService.discoveredHost': {
+ const host = discoveryNotify.params as Host;
+ this.discoveryResult.addHost(host);
+ break;
+ }
+ case 'DiscoveryService.discoveredPort': {
+ const port = discoveryNotify.params as Port;
+ // this.discoveryResult.addPort(port);
+ break;
+ }
+ case 'DiscoveryService.discoveredService': {
+ const service = discoveryNotify.params as Service;
+ this.discoveryFilter.addService(service);
+ this.discoveryResult.addService(service);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }),
+ catchError(error => {
+ return of();
+ }),
+ ).subscribe();
+ }
+
+ onRequestStop() {
+ this.discoverZone = null;
+ this.requested = false;
+ this.discoveryService.stopDiscovery(this.selectedProbe.probe.probeKey);
+ }
+}
diff --git a/@overflow/discovery/component2/index.ts b/@overflow/discovery/component2/index.ts
new file mode 100644
index 0000000..5a4df3a
--- /dev/null
+++ b/@overflow/discovery/component2/index.ts
@@ -0,0 +1,17 @@
+import { ServiceSelectorComponent } from './service-selector.component';
+import { DiscoveryComponent } from './discovery.component';
+import { SearchConfigComponent } from './search-config.component';
+import { SearchFilterComponent } from './search-filter.component';
+import { SearchResultComponent } from './search-result.component';
+import { IpInputComponent } from './ip-input.component';
+import { RequestSummaryComponent } from './request-summary.component';
+
+export const COMPONENTS = [
+ ServiceSelectorComponent,
+ DiscoveryComponent,
+ SearchConfigComponent,
+ SearchFilterComponent,
+ SearchResultComponent,
+ IpInputComponent,
+ RequestSummaryComponent,
+];
diff --git a/@overflow/discovery/component2/ip-input.component.html b/@overflow/discovery/component2/ip-input.component.html
new file mode 100644
index 0000000..c0cf9df
--- /dev/null
+++ b/@overflow/discovery/component2/ip-input.component.html
@@ -0,0 +1,11 @@
+
+ {{title}}
+ = 0"type="text" pInputText placeholder="127" maxlength="3" [(ngModel)]="first" value="{{first}}" (keyup)="onIpInput($event)">
+ .
+ 1" type="text" pInputText placeholder="0" maxlength="3" [(ngModel)]="second" value="{{second}}" (keyup)="onIpInput($event)">
+ .
+ 2" type="text" pInputText placeholder="0" maxlength="3" [(ngModel)]="third" value="{{third}}" (keyup)="onIpInput($event)">
+ .
+
+
+
diff --git a/@overflow/discovery/component2/ip-input.component.ts b/@overflow/discovery/component2/ip-input.component.ts
new file mode 100644
index 0000000..f42577c
--- /dev/null
+++ b/@overflow/discovery/component2/ip-input.component.ts
@@ -0,0 +1,57 @@
+import {
+ AfterContentInit,
+ Component,
+ EventEmitter,
+ Input,
+ Output,
+ OnInit,
+} from '@angular/core';
+
+@Component({
+ selector: 'of-ip-input',
+ templateUrl: './ip-input.component.html',
+})
+export class IpInputComponent implements OnInit, AfterContentInit {
+
+ first: string;
+ second: string;
+ third: string;
+ fourth: string;
+
+ @Input() hostIp: string;
+ @Input() title: string;
+ @Input() fixed: number;
+ @Output() inputIp = new EventEmitter();
+
+ constructor(
+ ) {
+ }
+
+ ngOnInit() {
+ if (this.hostIp !== '' && this.hostIp !== null && this.hostIp !== undefined) {
+ const tempIp = this.hostIp.split('.');
+
+ this.first = tempIp[0];
+ this.second = tempIp[1];
+ this.third = tempIp[2];
+ this.fourth = tempIp[3];
+ }
+ }
+
+ ngAfterContentInit() {
+ }
+
+ onIpInput(event) {
+ if (
+ (this.first !== '' && this.first !== undefined) &&
+ (this.second !== '' && this.second !== undefined) &&
+ (this.third !== '' && this.third !== undefined) &&
+ (this.fourth !== '' && this.fourth !== undefined) ) {
+ event.value = this.first + '.' + this.second + '.' + this.third + '.' + this.fourth;
+ this.inputIp.emit(event);
+ } else {
+ return;
+ }
+ }
+
+}
diff --git a/@overflow/discovery/component2/request-summary.component.html b/@overflow/discovery/component2/request-summary.component.html
new file mode 100644
index 0000000..8acc404
--- /dev/null
+++ b/@overflow/discovery/component2/request-summary.component.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Port type
+
+ TCP
+ UDP
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/@overflow/discovery/component2/request-summary.component.ts b/@overflow/discovery/component2/request-summary.component.ts
new file mode 100644
index 0000000..93a6217
--- /dev/null
+++ b/@overflow/discovery/component2/request-summary.component.ts
@@ -0,0 +1,33 @@
+import {
+ Component, Input, OnChanges, SimpleChanges,
+} from '@angular/core';
+import { DiscoverZone } from '@overflow/commons-typescript/model/discovery';
+
+@Component({
+ selector: 'of-discovery-request-summary',
+ templateUrl: './request-summary.component.html',
+})
+export class RequestSummaryComponent implements OnChanges {
+
+ @Input() discoverZone: DiscoverZone;
+ hostRange: string;
+ hostExclude: string;
+ portRange: string;
+ portExclude: string;
+ services: string[];
+
+ constructor(
+ ) {
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ this.hostRange = this.discoverZone.discoverHost.firstScanRange
+ + ' ~ '
+ + this.discoverZone.discoverHost.lastScanRange;
+ this.portRange = this.discoverZone.discoverHost.discoverPort.firstScanRange
+ + ' ~ '
+ + this.discoverZone.discoverHost.discoverPort.lastScanRange;
+ this.services = this.discoverZone.discoverHost.discoverPort.discoverService.includeServices;
+ }
+
+}
diff --git a/@overflow/discovery/component2/search-config.component.html b/@overflow/discovery/component2/search-config.component.html
new file mode 100644
index 0000000..c2e3211
--- /dev/null
+++ b/@overflow/discovery/component2/search-config.component.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ipErrMsg}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{portErrMsg}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add Ports to exclude.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/@overflow/discovery/component2/search-config.component.ts b/@overflow/discovery/component2/search-config.component.ts
new file mode 100644
index 0000000..aee9738
--- /dev/null
+++ b/@overflow/discovery/component2/search-config.component.ts
@@ -0,0 +1,311 @@
+import {
+ Component, EventEmitter, Input,
+ Output, OnChanges, SimpleChanges
+} from '@angular/core';
+import { ProbeHost } from '@overflow/commons-typescript/model/probe';
+import * as CIDR from 'ip-cidr';
+import * as ipRangeCheck from 'ip-range-check';
+import { DiscoverPort, DiscoverService, DiscoverZone } from '@overflow/commons-typescript/model/discovery';
+import { Checkbox } from 'primeng/primeng';
+import { MetaIPTypeEnum, toMetaIPType } from '@overflow/commons-typescript/model/meta';
+
+@Component({
+ selector: 'of-discovery-search-config',
+ templateUrl: './search-config.component.html',
+})
+export class SearchConfigComponent implements OnChanges {
+
+ SUPPORT_V6 = false;
+
+ @Input() probeHost: ProbeHost;
+ @Output() requestDiscovery = new EventEmitter();
+
+ ipType: MetaIPTypeEnum;
+ startIP: string;
+ endIP: string;
+ startPort: string;
+ endPort: string;
+ excludeIPs = [];
+ excludePorts = [];
+ includeServices = [];
+
+ hostChecked = true;
+ portChecked = true;
+ serviceChecked = true;
+ tcpChecked = true;
+ udpChecked = true;
+
+ validation = false;
+ ipErrMsg: string;
+ portErrMsg: string;
+
+ fixedIPrange: number;
+ ipArray = [];
+ portExcludeDisplay = false;
+ portChips = [];
+
+ constructor(
+ ) {
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes['probeHost']) {
+ this.initByProbe();
+ }
+ this.validation = true;
+ }
+
+ initByProbe() {
+ const cidr = new CIDR(this.probeHost.probe.cidr);
+ if (!cidr.isValid()) {
+ return;
+ }
+ this.ipType = MetaIPTypeEnum.V4;
+ this.startIP = cidr.start();
+ this.endIP = cidr.end();
+ this.startPort = '1';
+ this.endPort = '1024';
+ // TODO: Initialize services
+
+ this.checkEditableIPrange(cidr);
+ }
+
+ onIPExcludeFocus() {
+ const cidr = new CIDR(this.probeHost.probe.cidr);
+ this.ipArray = cidr.toArray().filter((value) => {
+ return this.inRange(value) || this.excludeIPs.find(obj => obj.ip === value);
+ }).map(value => {
+ return { ip: value };
+ });
+
+ }
+
+ inRange(value) {
+ const min = this.startIP;
+ const max = this.endIP;
+ if (this.ipToNum(min) <= this.ipToNum(value) && this.ipToNum(max) >= this.ipToNum(value)) {
+ return true;
+ }
+ return false;
+ }
+
+ ipToNum(ip) {
+ return Number(
+ ip.split('.')
+ .map(d => ('000' + d).substr(-3))
+ .join('')
+ );
+ }
+
+ onRequestDiscovery() {
+ let discoverPort: DiscoverPort = null;
+ let discoverService: DiscoverService = null;
+
+ if (this.serviceChecked) {
+ const services = [];
+ for (const service of this.includeServices) {
+ services.push(service.key);
+ }
+ discoverService = {
+ includeServices: services,
+ };
+ }
+ if (this.portChecked) {
+ discoverPort = {
+ firstScanRange: Number(this.startPort),
+ lastScanRange: Number(this.endPort),
+ includeTCP: this.tcpChecked,
+ includeUDP: this.udpChecked,
+ excludePorts: this.excludePorts,
+ discoverService: discoverService
+ };
+ }
+ const discoverZone: DiscoverZone = {
+ discoverHost: {
+ metaIPType: toMetaIPType(this.ipType),
+ firstScanRange: this.startIP,
+ lastScanRange: this.endIP,
+ discoverPort: discoverPort,
+ excludeHosts: this.excludeIPs,
+ },
+ };
+
+ console.log(discoverZone);
+ this.requestDiscovery.emit(discoverZone);
+ }
+
+ checkEditableIPrange(cidr) {
+ const startIPArray = cidr.addressStart.parsedAddress;
+ const endIPArray = cidr.addressEnd.parsedAddress;
+ let count = 0;
+ endIPArray.forEach((element, idx) => {
+ if (element === startIPArray[idx]) {
+ count++;
+ }
+ });
+ this.fixedIPrange = count;
+ }
+
+ onPortCheckChange(serviceCheckbox: Checkbox, checked: boolean) {
+ if (!checked) {
+ serviceCheckbox.checked = false;
+ this.serviceChecked = false;
+ } else {
+ if (!this.tcpChecked && !this.udpChecked) {
+ this.tcpChecked = true;
+ this.udpChecked = true;
+ }
+ }
+ }
+
+ onServiceCheckChange(portCheckbox: Checkbox, checked: boolean) {
+ if (checked) {
+ portCheckbox.checked = true;
+ this.portChecked = true;
+ }
+ }
+
+ onPortTypeCheck(portCheckbox: Checkbox, serviceCheckbox: Checkbox): void {
+ if (!this.tcpChecked && !this.udpChecked) {
+ this.portChecked = false;
+ portCheckbox.checked = false;
+ this.serviceChecked = false;
+ serviceCheckbox.checked = false;
+ }
+ }
+
+ onInputIP(e, idx: number) {
+ const value = e.value;
+ if (idx === 0) {
+ this.startIP = value;
+ } else {
+ this.endIP = value;
+ }
+ this.ipErrMsg = this.validateIP(value, idx);
+
+ if (this.ipErrMsg) {
+ this.validation = false;
+ } else {
+ this.validation = true;
+ }
+ }
+
+ validateIP(value: string, idx): string {
+ if (!this.isValidIPregex(value)) {
+ return 'Not valid IP format.';
+ }
+ if (!ipRangeCheck(value, this.probeHost.probe.cidr)) {
+ return 'Not valid IP range.';
+ }
+ const ipArray = [this.startIP, this.endIP];
+ const sortedIpArray = this.sortIP([this.startIP, this.endIP]);
+ if (ipArray[0] !== sortedIpArray[0]) {
+ return 'Not valiad range';
+ }
+
+ return null;
+ }
+
+ isValidIPregex(ip: string): boolean {
+ if (this.ipType === MetaIPTypeEnum.V4) {
+ return /^(?=\d+\.\d+\.\d+\.\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.?){4}$/.test(ip);
+ }
+ return false;
+ }
+
+ sortIP(ipAddressArray) {
+ return ipAddressArray.sort((a, b) => {
+ a = a.split('.');
+ b = b.split('.');
+ for (let i = 0; i < a.length; i++) {
+ // tslint:disable-next-line:radix
+ if ((a[i] = parseInt(a[i])) < (b[i] = parseInt(b[i]))) {
+ return -1;
+ } else if (a[i] > b[i]) {
+ return 1;
+ }
+ }
+ return 0;
+ });
+ }
+
+ onInputPort(portNum: string, idx: number) {
+ if (idx === 0) {
+ this.startPort = portNum;
+ } else {
+ this.endPort = portNum;
+ }
+ this.portErrMsg = this.validatePort(portNum);
+
+ if (this.portErrMsg) {
+ this.validation = false;
+ } else {
+ this.validation = true;
+ }
+ }
+
+ validatePort(portNum): string {
+ if (Number(this.startPort) > Number(this.endPort)) {
+ return 'Not valid port range.';
+ }
+ if (Number(portNum) <= 0 || Number(portNum) > 65535) {
+ return 'Not valid port range.';
+ }
+ return null;
+ }
+
+
+ onAddExcludePort(event) {
+ const port = event.value.replace(/\s/g, '');
+ if (port.indexOf('~') > 0) { // e.g. 1~3000
+ const splited = port.split('~');
+ this.portChips.pop();
+ const from = Number(splited[0]);
+ const to = Number(splited[1]);
+ if (this.checkInvalidPort(from)
+ || this.checkInvalidPort(to)
+ || !Number.isInteger(from)
+ || !Number.isInteger(to)) {
+ return;
+ }
+ const chipItem = 'All ' + from + ' ~ ' + to;
+ this.portChips.push(chipItem);
+ for (let i = from; i <= to; i++) {
+ this.excludePorts.push(String(i));
+ }
+ } else {
+ const num = Number(event.value);
+ if (!Number.isInteger(num) || this.checkInvalidPort(num) || this.checkExistExcludePort(event.value)) {
+ this.portChips.pop();
+ return;
+ }
+ this.excludePorts.push(event.value);
+ }
+ }
+
+ onRemoveExcludePort(event) {
+ if (event.value.indexOf('~') > 0) {
+ // e.g. 'All 1 ~ 30'
+ const splited = event.value.replace(/\s/g, '').replace('All', '').split('~');
+ for (let i = Number(splited[0]); i <= Number(splited[1]); i++) {
+ console.log(String(i));
+ const index = this.excludePorts.indexOf(String(i));
+ this.excludePorts.splice(index, 1);
+ }
+ return;
+ }
+ const idx = this.excludePorts.indexOf(event.value);
+ this.excludePorts.splice(idx, 1);
+ }
+
+ checkExistExcludePort(portNum) {
+ return this.excludePorts.find((value) => {
+ return value === portNum;
+ });
+ }
+
+ checkInvalidPort(port) {
+ return port <= 0 || port > 65535;
+ }
+
+}
diff --git a/@overflow/discovery/component2/search-filter.component.html b/@overflow/discovery/component2/search-filter.component.html
new file mode 100644
index 0000000..1300e62
--- /dev/null
+++ b/@overflow/discovery/component2/search-filter.component.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/@overflow/discovery/component2/search-filter.component.ts b/@overflow/discovery/component2/search-filter.component.ts
new file mode 100644
index 0000000..9b354f2
--- /dev/null
+++ b/@overflow/discovery/component2/search-filter.component.ts
@@ -0,0 +1,68 @@
+import {
+ Component,
+ OnInit,
+ Output,
+ EventEmitter
+} from '@angular/core';
+import { Anim } from './animation';
+import { Service } from '@overflow/commons-typescript/model/discovery';
+
+@Component({
+ selector: 'of-discovery-search-filter',
+ templateUrl: './search-filter.component.html',
+ animations: Anim
+})
+export class SearchFilterComponent implements OnInit {
+
+ services: Service[] = [];
+ filterWord: string;
+ filterServices = new Map();
+
+ @Output() search = new EventEmitter();
+ @Output() serviceSelect = new EventEmitter