Merge branch 'master' into master-2.3

This commit is contained in:
piotrpekala7 2021-02-02 01:15:32 +01:00
commit 7bc95954ec
85 changed files with 2625 additions and 2969 deletions

View File

@ -1,6 +1,5 @@
# gns3-web-ui
[![Known Vulnerabilities](https://snyk.io/test/github/GNS3/gns3-web-ui/badge.svg)](https://snyk.io/test/github/GNS3/gns3-web-ui)
[![Travis CI](https://api.travis-ci.org/GNS3/gns3-web-ui.svg?branch=master)](https://travis-ci.org)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/GNS3/gns3-web-ui?branch=master&svg=true)](https://www.appveyor.com/)
[![CircleCI](https://circleci.com/gh/GNS3/gns3-web-ui/tree/master.png)](https://circleci.com/gh/GNS3/gns3-web-ui/tree/master.png)
@ -10,10 +9,6 @@
[![Packages versions](https://repology.org/badge/latest-versions/gns3.svg)](https://repology.org/metapackage/gns3/versions)
[![Packages](https://repology.org/badge/tiny-repos/gns3.svg)](https://repology.org/metapackage/gns3/versions)
Test WebUI implementation for GNS3.
This is not production ready version. It has been made to evaluate possibility of creation Web User Interface for GNS3 application.
## Demo

View File

@ -33,7 +33,6 @@
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"extractCss": true,
"assets": [
"src/assets",
"src/favicon.ico",
@ -42,14 +41,14 @@
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/notosans-fontface/css/notosans-fontface.min.css",
"src/styles.css",
"src/styles.scss",
{
"inject": false,
"inject": true,
"input": "src/theme.scss",
"bundleName": "theme-default-dark"
},
{
"inject": false,
"inject": true,
"input": "src/theme-light.scss",
"bundleName": "theme-default"
}
@ -71,7 +70,6 @@
"scripts": true,
"styles": false
},
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@ -94,7 +92,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@ -131,7 +128,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@ -183,7 +179,7 @@
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/notosans-fontface/css/notosans-fontface.min.css",
"src/styles.css",
"src/styles.scss",
"src/theme.scss"
],
"assets": [
@ -208,6 +204,11 @@
]
}
}
},
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
}
},
"gns3-web-ui-e2e": {

1
debug.log Normal file
View File

@ -0,0 +1 @@
[1109/003452.026:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)

View File

@ -1,6 +1,6 @@
{
"name": "gns3-web-ui",
"version": "2.2.16dev1",
"version": "2.2.18dev",
"author": {
"name": "GNS3 Technology Inc.",
"email": "developers@gns3.com"
@ -40,39 +40,38 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^10.1.5",
"@angular/cdk": "^10.2.4",
"@angular/common": "^10.1.5",
"@angular/compiler": "^10.1.5",
"@angular/core": "^10.1.5",
"@angular/forms": "^10.1.5",
"@angular/animations": "^11.0.8",
"@angular/cdk": "^11.0.3",
"@angular/common": "^11.0.8",
"@angular/compiler": "^11.0.8",
"@angular/core": "^11.0.8",
"@angular/forms": "^11.0.8",
"@angular/http": "^7.2.16",
"@angular/material": "^10.2.4",
"@angular/platform-browser": "^10.1.5",
"@angular/platform-browser-dynamic": "^10.1.5",
"@angular/router": "^10.1.5",
"@sentry/browser": "^5.26.0",
"@types/jest": "^26.0.14",
"@types/mocha": "^8.0.3",
"angular-draggable-droppable": "^4.5.4",
"@angular/material": "^11.0.3",
"@angular/platform-browser": "^11.0.8",
"@angular/platform-browser-dynamic": "^11.0.8",
"@angular/router": "^11.0.8",
"@sentry/browser": "^5.29.2",
"@types/jest": "^26.0.20",
"@types/mocha": "^8.2.0",
"angular-draggable-droppable": "^4.6.0",
"angular-persistence": "^1.0.1",
"angular-resizable-element": "^3.3.3",
"angular-resizable-element": "^3.3.4",
"angular2-draggable": "^2.3.2",
"angular2-hotkeys": "^2.2.0",
"angular2-indexeddb": "^1.2.3",
"bootstrap": "4.5.2",
"bootstrap": "^4.5.3",
"command-exists": "^1.2.9",
"core-js": "^3.6.5",
"css-tree": "^1.0.0-alpha.36",
"core-js": "^3.8.2",
"d3-ng2-service": "^2.1.0",
"file-saver": "^2.0.2",
"ini": "^1.3.5",
"marked": "^1.1.1",
"file-saver": "^2.0.5",
"ini": "^1.3.8",
"material-design-icons": "^3.0.1",
"ng-circle-progress": "^1.6.0",
"ng2-file-upload": "^1.3.0",
"ngx-childprocess": "^0.0.6",
"ngx-device-detector": "^2.0.0",
"ngx-device-detector": "^2.0.5",
"ngx-electron": "^2.1.1",
"node-fetch": "^2.6.1",
"notosans-fontface": "1.2.2",
@ -80,44 +79,45 @@
"rxjs-compat": "^6.6.3",
"save-html-as-image": "^1.3.4",
"save-svg-as-png": "^1.4.14",
"snyk": "^1.413.3",
"svg-crowbar": "^0.6.1",
"schematics-scss-migrate": "^1.2.10",
"snyk": "^1.437.3",
"spark-md5": "^3.0.1",
"svg-crowbar": "^0.6.5",
"tree-kill": "^1.2.1",
"tslib": "^2.0.3",
"typeface-roboto": "^0.0.75",
"tslib": "^2.1.0",
"typeface-roboto": "^1.1.13",
"xterm": "^4.9.0",
"xterm-addon-attach": "^0.6.0",
"xterm-addon-fit": "^0.4.0",
"yargs": "^16.0.3",
"zone.js": "~0.11.1"
"yargs": "^16.2.0",
"zone.js": "^0.11.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.1001.6",
"@angular/cli": "^10.1.6",
"@angular/compiler-cli": "^10.1.5",
"@angular/language-service": "^10.1.5",
"@sentry/cli": "^1.58.0",
"@sentry/electron": "^2.0.1",
"@types/jasmine": "^3.5.14",
"@angular-devkit/build-angular": "^0.1100.6",
"@angular/cli": "^11.0.6",
"@angular/compiler-cli": "^11.0.8",
"@angular/language-service": "^11.0.8",
"@sentry/cli": "^1.61.0",
"@sentry/electron": "^2.1.0",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "^2.0.8",
"@types/node": "14.11.2",
"codelyzer": "^6.0.1",
"electron": "^10.1.3",
"electron-builder": "22.8.1",
"file-loader": "^6.1.1",
"jasmine-core": "^3.6.0",
"jasmine-spec-reporter": "^6.0.0",
"@types/node": "14.14.10",
"codelyzer": "^6.0.0",
"electron": "^11.2.0",
"electron-builder": "22.9.1",
"file-loader": "^6.2.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"jquery": "^3.5.1",
"karma": "^5.2.3",
"karma-chrome-launcher": "^3.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-cli": "^2.0.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "^4.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"license-checker": "^25.0.1",
"node-sass": "^4.14.1",
"popper.js": "^1.16.1",
"prettier": "^2.1.2",
"prettier": "^2.2.1",
"protractor": "^7.0.0",
"replace": "^1.2.0",
"rxjs-tslint": "^0.1.8",
@ -125,8 +125,9 @@
"ts-node": "~9.0.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.0.3",
"webpack": "^4.44.2"
"typescript": "4.0.2",
"webpack": "4.44.2",
"yarn-upgrade-all": "^0.5.4"
},
"greenkeeper": {
"ignore": [

View File

@ -10,7 +10,7 @@ import { ProgressService } from './common/progress/progress.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
constructor(

View File

@ -7,7 +7,6 @@ import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { D3Service } from 'd3-ng2-service';
import { HotkeyModule } from 'angular2-hotkeys';
import { PersistenceModule } from 'angular-persistence';
import { NgxElectronModule } from 'ngx-electron';
import { FileUploadModule } from 'ng2-file-upload';
@ -54,7 +53,6 @@ import { DrawingsDataSource } from './cartography/datasources/drawings-datasourc
import { EditStyleActionComponent } from './components/project-map/context-menu/actions/edit-style-action/edit-style-action.component';
import { MoveLayerDownActionComponent } from './components/project-map/context-menu/actions/move-layer-down-action/move-layer-down-action.component';
import { MoveLayerUpActionComponent } from './components/project-map/context-menu/actions/move-layer-up-action/move-layer-up-action.component';
import { ProjectMapShortcutsComponent } from './components/project-map/project-map-shortcuts/project-map-shortcuts.component';
import { SettingsComponent } from './components/settings/settings.component';
import { SettingsService } from './services/settings.service';
@ -321,7 +319,6 @@ import { ProjectReadmeComponent } from './components/project-map/project-readme/
ResumeLinkActionComponent,
SuspendLinkActionComponent,
ResetLinkActionComponent,
ProjectMapShortcutsComponent,
SettingsComponent,
PreferencesComponent,
BundledServerFinderComponent,
@ -489,7 +486,6 @@ import { ProjectReadmeComponent } from './components/project-map/project-readme/
BrowserAnimationsModule,
CdkTableModule,
CartographyModule,
HotkeyModule.forRoot(),
PersistenceModule,
NgxElectronModule,
FileUploadModule,

View File

@ -103,8 +103,12 @@ export class NodeWidget implements Widget {
if (n.height > 64) return 64;
return n.height;
})
.attr('x', (n: MapNode) => 0)
.attr('y', (n: MapNode) => 0)
.attr('x', (n: MapNode) => {
return 0
})
.attr('y', (n: MapNode) => {
return 0
})
.on('mouseover', function(this, n: MapNode) {
select(this).attr('class', 'over');
})
@ -113,6 +117,7 @@ export class NodeWidget implements Widget {
});
node_body_merge.attr('transform', (n: MapNode) => {
if (!n.width) return `translate(${n.x - 30},${n.y - 30})`
return `translate(${n.x},${n.y})`;
});

View File

@ -4,7 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-information-dialog',
templateUrl: 'information-dialog.component.html',
styleUrls: ['information-dialog.component.css']
styleUrls: ['information-dialog.component.scss']
})
export class InformationDialogComponent implements OnInit {
public confirmationMessage: string;

View File

@ -13,7 +13,7 @@ import { AddedDataEvent } from '../../../cartography/events/event-source';
@Component({
selector: 'app-drawing-added',
templateUrl: './drawing-added.component.html',
styleUrls: ['./drawing-added.component.css']
styleUrls: ['./drawing-added.component.scss']
})
export class DrawingAddedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -12,7 +12,7 @@ import { Project } from '../../../models/project';
@Component({
selector: 'app-drawing-dragged',
templateUrl: './drawing-dragged.component.html',
styleUrls: ['./drawing-dragged.component.css']
styleUrls: ['./drawing-dragged.component.scss']
})
export class DrawingDraggedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -12,7 +12,7 @@ import { Subscription } from 'rxjs';
@Component({
selector: 'app-drawing-resized',
templateUrl: './drawing-resized.component.html',
styleUrls: ['./drawing-resized.component.css']
styleUrls: ['./drawing-resized.component.scss']
})
export class DrawingResizedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -11,7 +11,7 @@ import { LinksEventSource } from '../../../cartography/events/links-event-source
@Component({
selector: 'app-interface-label-dragged',
templateUrl: './interface-label-dragged.component.html',
styleUrls: ['./interface-label-dragged.component.css']
styleUrls: ['./interface-label-dragged.component.scss']
})
export class InterfaceLabelDraggedComponent {
@Input() server: Server;

View File

@ -14,7 +14,7 @@ import { LinksEventSource } from '../../../cartography/events/links-event-source
@Component({
selector: 'app-link-created',
templateUrl: './link-created.component.html',
styleUrls: ['./link-created.component.css']
styleUrls: ['./link-created.component.scss']
})
export class LinkCreatedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -12,7 +12,7 @@ import { Project } from '../../../models/project';
@Component({
selector: 'app-node-dragged',
templateUrl: './node-dragged.component.html',
styleUrls: ['./node-dragged.component.css']
styleUrls: ['./node-dragged.component.scss']
})
export class NodeDraggedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -12,7 +12,7 @@ import { MapLabel } from '../../../cartography/models/map/map-label';
@Component({
selector: 'app-node-label-dragged',
templateUrl: './node-label-dragged.component.html',
styleUrls: ['./node-label-dragged.component.css']
styleUrls: ['./node-label-dragged.component.scss']
})
export class NodeLabelDraggedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -15,7 +15,7 @@ import { Context } from '../../../cartography/models/context';
@Component({
selector: 'app-text-added',
templateUrl: './text-added.component.html',
styleUrls: ['./text-added.component.css']
styleUrls: ['./text-added.component.scss']
})
export class TextAddedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -13,7 +13,7 @@ import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/ma
@Component({
selector: 'app-text-edited',
templateUrl: './text-edited.component.html',
styleUrls: ['./text-edited.component.css']
styleUrls: ['./text-edited.component.scss']
})
export class TextEditedComponent implements OnInit, OnDestroy {
@Input() server: Server;

View File

@ -26,7 +26,7 @@
<div class="wrapper">
<div class="buttonWrapper" *ngFor="let symbol of filteredSymbols | filenamefilter: searchText">
<button [ngClass]="{ buttonSelected: isSelected === symbol.symbol_id }" class="button" (click)="setSelected(symbol.symbol_id)">
<img [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" [src]="getImageSourceForTemplate(symbol.symbol_id)"/>
<img lazyimg [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" [src]="getImageSourceForTemplate(symbol.symbol_id)"/>
</button>
</div>
</div>

View File

@ -78,6 +78,6 @@ export class SymbolsComponent implements OnInit {
}
getImageSourceForTemplate(symbol: string) {
return `http://${this.server.host}:${this.server.port}/v2/symbols/${symbol}/raw`;
return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${symbol}/raw`;
}
}

View File

@ -1,3 +1,9 @@
@media screen and (max-width: 700px) {
.consoleWrapper {
visibility: hidden;
}
}
.consoleWrapper {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
position: fixed;

View File

@ -0,0 +1,33 @@
import { ConsoleWrapperComponent } from "./console-wrapper.component";
import { NodeConsoleService } from '../../../services/nodeConsole.service';
import { ThemeService } from '../../../services/theme.service';
import { MapSettingsService } from '../../../services/mapsettings.service';
describe('ConsoleWrapperComponent', () => {
it('should get actual theme', () => {
const consoleService = new NodeConsoleService();
const themeService = autoSpy(ThemeService);
themeService.getActualTheme.and.returnValue('light');
const mapSettingsService = autoSpy(MapSettingsService);
const component = new ConsoleWrapperComponent(consoleService, themeService, mapSettingsService);
component.ngOnInit();
expect(component.isLightThemeEnabled).toBe(true);
});
});
export type SpyOf<T> = T & { [k in keyof T]: jasmine.Spy };
export function autoSpy<T>(obj: new (...args: any[]) => T): SpyOf<T> {
const res: SpyOf<T> = {} as any;
const keys = Object.getOwnPropertyNames(obj.prototype);
keys.forEach((key) => {
res[key] = jasmine.createSpy(key);
});
return res;
}

View File

@ -17,7 +17,7 @@ import { Node } from '../../../cartography/models/node';
import { ConsoleDeviceActionComponent } from '../context-menu/actions/console-device-action/console-device-action.component';
import { ConsoleDeviceActionBrowserComponent } from '../context-menu/actions/console-device-action-browser/console-device-action-browser.component';
fdescribe('ContextConsoleMenuComponent', () => {
describe('ContextConsoleMenuComponent', () => {
let component: ContextConsoleMenuComponent;
let fixture: ComponentFixture<ContextConsoleMenuComponent>;
let toasterService: MockedToasterService = new MockedToasterService();

View File

@ -154,6 +154,6 @@ export class ImportApplianceComponent implements OnInit {
}
private getUploadPath(server: Server, emulator: string, filename: string) {
return `http://${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
}
}

View File

@ -76,7 +76,7 @@ describe('LogConsoleComponent', () => {
component.handleCommand();
expect(component.showMessage).toHaveBeenCalledWith({type: 'command', message: 'Current version: 2020.3.0-beta.4'});
expect(component.showMessage).toHaveBeenCalledWith({type: 'command', message: 'Current version: 2.2.18dev'});
});
it('should call show message when unknown command entered', () => {

View File

@ -167,7 +167,7 @@
type="file"
class="non-visible"
#file2
(change)="importImage($event)"
(change)="importImage($event, version.images.hda_disk_image)"
ng2FileSelect
[uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
@ -187,11 +187,11 @@
<input
type="file"
class="non-visible"
#file2
(change)="importImage($event)"
#file3
(change)="importImage($event, version.images.hdb_disk_image)"
ng2FileSelect
[uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
<button class="button" mat-raised-button (click)="file3.click()">Import</button>
<button class="button" mat-raised-button (click)="downloadImageFromVersion(version.images.hdb_disk_image)">Download</button>
</div>
</div>
@ -235,7 +235,7 @@
type="file"
class="non-visible"
#file2
(change)="importImage($event)"
(change)="importImage($event, image.filename)"
ng2FileSelect
[uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
@ -271,7 +271,7 @@
type="file"
class="non-visible"
#file2
(change)="importImage($event)"
(change)="importImage($, image.filename)"
ng2FileSelect
[uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button>

View File

@ -30,6 +30,7 @@ import { ComputeService } from '../../../services/compute.service';
import { InformationDialogComponent } from '../../../components/dialogs/information-dialog.component';
import { ProgressService } from '../../../common/progress/progress.service';
import { TemplateNameDialogComponent } from './template-name-dialog/template-name-dialog.component';
import * as SparkMD5 from 'spark-md5';
@Component({
selector: 'app-new-template-dialog',
@ -166,12 +167,14 @@ export class NewTemplateDialogComponent implements OnInit {
this.uploaderImage.onErrorItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.error('An error has occured');
this.progressService.deactivate();
this.uploaderImage.clearQueue();
};
this.uploaderImage.onSuccessItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.success('Image imported succesfully');
this.refreshImages();
this.progressService.deactivate();
this.uploaderImage.clearQueue();
};
}
@ -332,7 +335,35 @@ export class NewTemplateDialogComponent implements OnInit {
dialogRef.componentInstance.appliance = object;
}
importImage(event) {
importImage(event, imageName) {
this.progressService.activate();
this.computeChecksumMd5(event.target.files[0], false).then((output) => {
let imageToInstall = this.applianceToInstall.images.filter(n => n.filename === imageName)[0];
if (imageToInstall.md5sum !== output) {
this.progressService.deactivate();
const dialogRef = this.dialog.open(InformationDialogComponent, {
width: '400px',
height: '200px',
autoFocus: false,
disableClose: true
});
dialogRef.componentInstance.confirmationMessage = `This is not the correct file.
The MD5 sum is ${output} and should be ${imageToInstall.md5sum}. Do you want to accept it at your own risks?`;
dialogRef.afterClosed().subscribe((answer: boolean) => {
if (answer) {
this.importImageFile(event)
} else {
this.uploaderImage.clearQueue();
}
});
} else {
this.importImageFile(event);
}
});
}
importImageFile(event) {
let name = event.target.files[0].name.split('-')[0];
let fileName = event.target.files[0].name;
let file = event.target.files[0];
@ -373,10 +404,10 @@ export class NewTemplateDialogComponent implements OnInit {
checkImages(version: Version): boolean {
if (version.images.hdb_disk_image) {
if (this.checkImageFromVersion(version.images.hda_disk_image) && this.checkImageFromVersion(version.images.hdb_disk_image)) return true;
return false;
}
if (this.checkImageFromVersion(version.images.hda_disk_image)) return true;
return false;
}
@ -611,6 +642,36 @@ export class NewTemplateDialogComponent implements OnInit {
}
});
}
private computeChecksumMd5(file: File, encode = false): Promise<string> {
return new Promise((resolve, reject) => {
const chunkSize = 2097152;
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
let cursor = 0;
fileReader.onerror = function(): void {
reject('MD5 computation failed - error reading the file');
};
function processChunk(chunkStart: number): void {
const chunkEnd = Math.min(file.size, chunkStart + chunkSize);
fileReader.readAsArrayBuffer(file.slice(chunkStart, chunkEnd));
}
fileReader.onload = function(e: any): void {
spark.append(e.target.result);
cursor += chunkSize;
if (cursor < file.size) {
processChunk(cursor);
} else {
resolve(spark.end(encode));
}
};
processChunk(0);
});
}
}
function compareNames(a: string, b: string, isAsc: boolean) {

View File

@ -1,114 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { inject } from '@angular/core/testing';
import { mock, instance, capture, when, anything } from 'ts-mockito';
import { HotkeyModule, HotkeysService, Hotkey } from 'angular2-hotkeys';
import { of } from 'rxjs';
import { ProjectMapShortcutsComponent } from './project-map-shortcuts.component';
import { ToasterService } from '../../../services/toaster.service';
import { NodeService } from '../../../services/node.service';
import { HttpServer } from '../../../services/http-server.service';
import { SelectionManager } from '../../../cartography/managers/selection-manager';
import { Server } from '../../../models/server';
import { Project } from '../../../models/project';
import { ProjectService } from '../../../services/project.service';
import { MockedProjectService } from '../../../services/project.service.spec';
import { SettingsService } from '../../../services/settings.service';
import { MockedToasterService } from '../../../services/toaster.service.spec';
import { mapTo } from 'rxjs/internal/operators';
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
import { MapNode } from '../../../cartography/models/map/map-node';
import { MapLabelToLabelConverter } from '../../../cartography/converters/map/map-label-to-label-converter';
import { MapPortToPortConverter } from '../../../cartography/converters/map/map-port-to-port-converter';
import { Node } from '../../../cartography/models/node';
import { FontBBoxCalculator } from '../../../cartography/helpers/font-bbox-calculator';
import { CssFixer } from '../../../cartography/helpers/css-fixer';
import { FontFixer } from '../../../cartography/helpers/font-fixer';
describe('ProjectMapShortcutsComponent', () => {
let component: ProjectMapShortcutsComponent;
let fixture: ComponentFixture<ProjectMapShortcutsComponent>;
let hotkeyServiceMock: HotkeysService;
let hotkeyServiceInstanceMock: HotkeysService;
let nodeServiceMock: NodeService;
let node: MapNode;
beforeEach(async(() => {
node = new MapNode();
const selectionManagerMock = mock(SelectionManager);
when(selectionManagerMock.getSelected()).thenReturn([node]);
nodeServiceMock = mock(NodeService);
hotkeyServiceMock = mock(HotkeysService);
hotkeyServiceInstanceMock = instance(hotkeyServiceMock);
TestBed.configureTestingModule({
imports: [HotkeyModule.forRoot(), HttpClientTestingModule],
providers: [
HttpServer,
{ provide: NodeService, useFactory: () => instance(nodeServiceMock) },
{ provide: HotkeysService, useFactory: () => hotkeyServiceInstanceMock },
{ provide: ToasterService, useClass: MockedToasterService },
{ provide: ProjectService, useClass: MockedProjectService },
{ provide: SelectionManager, useValue: instance(selectionManagerMock) },
SettingsService,
MapNodeToNodeConverter,
MapLabelToLabelConverter,
MapPortToPortConverter,
MapLabelToLabelConverter,
FontBBoxCalculator,
CssFixer,
FontFixer
],
declarations: [ProjectMapShortcutsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProjectMapShortcutsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should bind delete key', () => {
component.ngOnInit();
const [hotkey] = capture(hotkeyServiceMock.add).last();
expect((hotkey as Hotkey).combo).toEqual(['del']);
expect((hotkey as Hotkey).callback).toBeDefined();
});
it('should remove binding', () => {
component.ngOnDestroy();
const [hotkey] = capture(hotkeyServiceMock.remove).last();
expect((hotkey as Hotkey).combo).toEqual(['del']);
});
describe('onDeleteHandler', () => {
beforeEach(() => {
const server = new Server();
const project = new Project();
when(nodeServiceMock.delete(server, anything())).thenReturn(of(new Node()).pipe(mapTo(null)));
component.project = project;
component.server = server;
});
it('should handle delete', inject([ToasterService], (toaster: MockedToasterService) => {
component.project.readonly = false;
component.onDeleteHandler(null);
expect(toaster.successes).toEqual(['Node has been deleted']);
}));
it('should not delete when project in readonly mode', inject([ToasterService], (toaster: MockedToasterService) => {
component.project.readonly = true;
component.onDeleteHandler(null);
expect(toaster.successes).toEqual([]);
}));
});
});

View File

@ -1,59 +0,0 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { HotkeysService, Hotkey } from 'angular2-hotkeys';
import { SelectionManager } from '../../../cartography/managers/selection-manager';
import { NodeService } from '../../../services/node.service';
import { Server } from '../../../models/server';
import { ToasterService } from '../../../services/toaster.service';
import { Project } from '../../../models/project';
import { ProjectService } from '../../../services/project.service';
import { MapNode } from '../../../cartography/models/map/map-node';
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
@Component({
selector: 'app-project-map-shortcuts',
template: ''
})
export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
@Input() project: Project;
@Input() server: Server;
private deleteHotkey: Hotkey;
constructor(
private hotkeysService: HotkeysService,
private toaster: ToasterService,
private nodesService: NodeService,
private projectService: ProjectService,
private mapNodeToNode: MapNodeToNodeConverter,
private selectionManager: SelectionManager
) {}
ngOnInit() {
const self = this;
this.deleteHotkey = new Hotkey('del', (event: KeyboardEvent) => {
return self.onDeleteHandler(event);
});
this.hotkeysService.add(this.deleteHotkey);
}
onDeleteHandler(event: KeyboardEvent): boolean {
if (!this.projectService.isReadOnly(this.project)) {
const selected = this.selectionManager.getSelected();
selected
.filter(item => item instanceof MapNode)
.forEach((item: MapNode) => {
const node = this.mapNodeToNode.convert(item);
this.nodesService.delete(this.server, node).subscribe(data => {
this.toaster.success('Node has been deleted');
});
});
}
return false;
}
ngOnDestroy() {
this.hotkeysService.remove(this.deleteHotkey);
}
}

View File

@ -193,7 +193,6 @@
</div>
<app-progress></app-progress>
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server"></app-project-map-shortcuts>
<app-draw-link-tool [links]="links" *ngIf="tools.draw_link"></app-draw-link-tool>
<app-drawing-dragged [server]="server" [project]="project"></app-drawing-dragged>

View File

@ -73,6 +73,7 @@ import { Title } from '@angular/platform-browser';
import { NewTemplateDialogComponent } from './new-template-dialog/new-template-dialog.component';
import { NodeConsoleService } from '../../services/nodeConsole.service';
import { ProjectReadmeComponent } from './project-readme/project-readme.component';
import * as Mousetrap from 'mousetrap';
@Component({
@ -168,12 +169,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
) {}
ngOnInit() {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
this.settings = this.settingsService.getAll();
this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible;
this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible;
this.mapSettingsService.logConsoleSubject.subscribe(value => this.isConsoleVisible = value);
this.getSettings();
this.progressService.activate();
if (this.serverService.isServiceInitialized) {
@ -186,6 +182,22 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
);
}
this.addSubscriptions();
this.addKeyboardListeners();
}
getSettings() {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
this.settings = this.settingsService.getAll();
this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible;
this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible;
this.mapSettingsService.logConsoleSubject.subscribe(value => this.isConsoleVisible = value);
this.notificationsVisibility = localStorage.getItem('notificationsVisibility') === 'true' ? true : false;
this.layersVisibility = localStorage.getItem('layersVisibility') === 'true' ? true : false;
this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? true : false;
}
addSubscriptions() {
this.projectMapSubscription.add(
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
if (this.scrollEnabled) this.centerCanvas();
@ -203,7 +215,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
if (!this.server) return;
nodes.forEach((node: Node) => {
node.symbol_url = `http://${this.server.host}:${this.server.port}/v2/symbols/${node.symbol}/raw`;
node.symbol_url = `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${node.symbol}/raw`;
});
this.nodes = nodes;
@ -231,11 +243,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
message: message
});
}));
this.notificationsVisibility = localStorage.getItem('notificationsVisibility') === 'true' ? true : false;
this.layersVisibility = localStorage.getItem('layersVisibility') === 'true' ? true : false;
this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? true : false;
this.addKeyboardListeners();
}
getData() {
@ -330,6 +337,20 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
event.preventDefault();
this.router.navigate(['/server', this.server.id, 'preferences']);
});
Mousetrap.bind('del', (event: Event) => {
event.preventDefault();
const selected = this.selectionManager.getSelected();
selected
.filter(item => item instanceof MapNode)
.forEach((item: MapNode) => {
const node = this.mapNodeToNode.convert(item);
this.nodeService.delete(this.server, node).subscribe(data => {
this.toasterService.success('Node has been deleted');
});
});
});
}
onProjectLoad(project: Project) {

View File

@ -15,7 +15,7 @@ import { projectNameAsyncValidator } from '../../../validators/project-name-asyn
@Component({
selector: 'app-add-blank-project-dialog',
templateUrl: './add-blank-project-dialog.component.html',
styleUrls: ['./add-blank-project-dialog.component.css'],
styleUrls: ['./add-blank-project-dialog.component.scss'],
providers: [ProjectNameValidator]
})
export class AddBlankProjectDialogComponent implements OnInit {

View File

@ -5,7 +5,7 @@ import { Project } from '../../../models/project';
@Component({
selector: 'app-import-project-dialog',
templateUrl: 'confirmation-dialog.component.html',
styleUrls: ['confirmation-dialog.component.css']
styleUrls: ['confirmation-dialog.component.scss']
})
export class ConfirmationDialogComponent implements OnInit {
private existingProject: Project;

View File

@ -13,7 +13,7 @@ import { ProjectNameValidator } from '../models/projectNameValidator';
@Component({
selector: 'app-import-project-dialog',
templateUrl: 'import-project-dialog.component.html',
styleUrls: ['import-project-dialog.component.css'],
styleUrls: ['import-project-dialog.component.scss'],
providers: [ProjectNameValidator]
})
export class ImportProjectDialogComponent implements OnInit {

View File

@ -28,7 +28,7 @@ import { ElectronService } from 'ngx-electron';
@Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.css']
styleUrls: ['./projects.component.scss']
})
export class ProjectsComponent implements OnInit {
server: Server;

View File

@ -14,7 +14,7 @@ import { NodesDataSource } from '../../../cartography/datasources/nodes-datasour
@Component({
selector: 'app-save-project-dialog',
templateUrl: './save-project-dialog.component.html',
styleUrls: ['./save-project-dialog.component.css'],
styleUrls: ['./save-project-dialog.component.scss'],
providers: [ProjectNameValidator]
})
export class SaveProjectDialogComponent implements OnInit {

View File

@ -28,6 +28,12 @@
<input matInput tabindex="1" formControlName="port" placeholder="Port" />
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Protocol" formControlName="protocol" >
<mat-option *ngFor="let protocol of protocols" [value]="protocol.key"> {{ protocol.name }} </mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="serverForm.get('location').value === 'remote'">
<mat-select placeholder="Authorization" formControlName="authorization" >
<mat-option *ngFor="let auth of authorizations" [value]="auth.key"> {{ auth.name }} </mat-option>

View File

@ -13,6 +13,7 @@ import { ToasterService } from '../../../services/toaster.service';
})
export class AddServerDialogComponent implements OnInit {
authorizations = [{ key: 'none', name: 'No authorization' }, { key: 'basic', name: 'Basic authorization' }];
protocols = [{ key: 'http:', name: 'HTTP' }, { key: 'https:', name: 'HTTPS' }];
locations = [];
serverForm = new FormGroup({
@ -22,6 +23,7 @@ export class AddServerDialogComponent implements OnInit {
'ubridge_path': new FormControl(''),
'host': new FormControl('', [ Validators.required ]),
'port': new FormControl('', [ Validators.required, Validators.min(1) ]),
'protocol': new FormControl('http:'),
'authorization': new FormControl('none'),
'login': new FormControl(''),
'password': new FormControl('')

View File

@ -3,13 +3,14 @@ import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { map } from 'rxjs//operators';
import { Server } from '../../../models/server';
import { Server, ServerProtocol } from '../../../models/server';
import { VersionService } from '../../../services/version.service';
import { Version } from '../../../models/version';
import { forkJoin } from 'rxjs';
import { ServerService } from '../../../services/server.service';
import { ServerDatabase } from '../../../services/server.database';
import { from } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-server-discovery',
@ -29,24 +30,52 @@ export class ServerDiscoveryComponent implements OnInit {
constructor(
private versionService: VersionService,
private serverService: ServerService,
private serverDatabase: ServerDatabase
private serverDatabase: ServerDatabase,
private route : ActivatedRoute
) {}
ngOnInit() {
if (this.serverService.isServiceInitialized) this.discoverFirstAvailableServer();
if (this.serverService.isServiceInitialized) this.discoverFirstServer();
this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
if (value) {
this.discoverFirstAvailableServer();
this.discoverFirstServer();
}
});
}
async discoverFirstServer() {
let discovered = await this.discoverServers();
let local = await this.serverService.findAll();
local.forEach(added => {
discovered = discovered.filter(server => {
return !(server.host == added.host && server.port == added.port);
});
});
if (discovered.length > 0) {
this.discoveredServer = discovered.shift();
};
}
async discoverServers() {
let discoveredServers: Server[] = [];
this.defaultServers.forEach(async (testServer) => {
const server = new Server();
server.host = testServer.host;
server.port = testServer.port;
let version = await this.versionService.get(server).toPromise().catch(error => null);
if (version) discoveredServers.push(server);
});
return discoveredServers;
}
discoverFirstAvailableServer() {
forkJoin(
[from(this.serverService.findAll()).pipe(map((s: Server[]) => s)),
this.discovery()]
).subscribe(([local, discovered]) => {
).subscribe(
([local, discovered]) => {
local.forEach(added => {
discovered = discovered.filter(server => {
return !(server.host == added.host && server.port == added.port);
@ -55,7 +84,8 @@ export class ServerDiscoveryComponent implements OnInit {
if (discovered.length > 0) {
this.discoveredServer = discovered.shift();
}
});
},
error => {});
}
discovery(): Observable<Server[]> {
@ -94,6 +124,7 @@ export class ServerDiscoveryComponent implements OnInit {
}
server.location = 'remote';
server.protocol = location.protocol as ServerProtocol;
this.serverService.create(server).then((created: Server) => {
this.serverDatabase.addServer(created);

View File

@ -4,8 +4,8 @@ import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Observable, merge, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';
import { Server } from '../../models/server';
import { ActivatedRoute, Router } from '@angular/router';
import { Server, ServerProtocol } from '../../models/server';
import { ServerService } from '../../services/server.service';
import { ServerDatabase } from '../../services/server.database';
import { AddServerDialogComponent } from './add-server-dialog/add-server-dialog.component';
@ -18,7 +18,7 @@ import { ConfirmationBottomSheetComponent } from '../projects/confirmation-botto
@Component({
selector: 'app-server-list',
templateUrl: './servers.component.html',
styleUrls: ['./servers.component.css']
styleUrls: ['./servers.component.scss']
})
export class ServersComponent implements OnInit, OnDestroy {
dataSource: ServerDataSource;
@ -35,6 +35,7 @@ export class ServersComponent implements OnInit, OnDestroy {
private electronService: ElectronService,
private childProcessService: ChildProcessService,
private bottomSheet: MatBottomSheet,
private route : ActivatedRoute
) {}
getServers() {
@ -52,6 +53,7 @@ export class ServersComponent implements OnInit, OnDestroy {
this.serverService.checkServerVersion(server).subscribe(
(serverInfo) => {
if ((serverInfo.version.split('.')[1]>=2) && (serverInfo.version.split('.')[0]>=2)) {
if (!server.protocol) server.protocol = location.protocol as ServerProtocol;
if (!this.serverDatabase.find(server.name)) this.serverDatabase.addServer(server);
}
},

View File

@ -15,6 +15,8 @@ import { ToasterService } from '../../services/toaster.service';
import { MockedToasterService } from '../../services/toaster.service.spec';
import { ConsoleService } from '../../services/settings/console.service';
import { MapSettingsService } from '../../services/mapsettings.service';
import { UpdatesService } from '../../services/updates.service';
import { autoSpy } from '../project-map/console-wrapper/console-wrapper.component.spec';
describe('SettingsComponent', () => {
let component: SettingsComponent;
@ -25,6 +27,7 @@ describe('SettingsComponent', () => {
toggleIntegrateInterfaceLabels(val: boolean) {}
};
let consoleService;
let updatesService = autoSpy(UpdatesService);
beforeEach(async(() => {
consoleService = {
@ -37,7 +40,8 @@ describe('SettingsComponent', () => {
SettingsService,
{ provide: ToasterService, useClass: MockedToasterService },
{ provide: ConsoleService, useValue: consoleService },
{ provide: MapSettingsService, useValue: mapSettingsService }
{ provide: MapSettingsService, useValue: mapSettingsService },
{ provide: UpdatesService, useValue: updatesService }
],
declarations: [SettingsComponent]
}).compileComponents();

View File

@ -116,7 +116,7 @@ export class TemplateComponent implements OnInit, OnDestroy {
}
getImageSourceForTemplate(template: Template) {
return `http://${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`;
return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`;
}
ngOnDestroy() {

View File

@ -72,6 +72,12 @@
<div class="summaryContentServers">
<div class="nodeRow" *ngFor="let compute of computes">
<div>
<svg *ngIf="compute.connected" width="10" height="10">
<rect class="status_started" x="0" y="0" width="10" height="10" fill="green"></rect>
</svg>
<svg *ngIf="!compute.connected" width="10" height="10">
<rect class="status_stopped" x="0" y="0" width="10" height="10" fill="red"></rect>
</svg>
{{compute.name}}
</div>
<div>

View File

@ -1,3 +1,9 @@
@media screen and (max-width: 600px) {
.summaryWrapper {
visibility: hidden;
}
}
.summaryWrapper {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
position: fixed;

View File

@ -0,0 +1,12 @@
import { Directive, ElementRef } from '@angular/core';
@Directive({ selector: '[lazyimg]' })
export class LazyImgDirective {
constructor({ nativeElement }: ElementRef<HTMLImageElement>) {
const supports = 'loading' in HTMLImageElement.prototype;
if (supports) {
nativeElement.setAttribute('loading', 'lazy');
}
}
}

View File

@ -13,7 +13,7 @@ import { Router } from '@angular/router';
selector: 'app-default-layout',
encapsulation: ViewEncapsulation.None,
templateUrl: './default-layout.component.html',
styleUrls: ['./default-layout.component.css']
styleUrls: ['./default-layout.component.scss']
})
export class DefaultLayoutComponent implements OnInit, OnDestroy {
public isInstalledSoftwareAvailable = false;

View File

@ -1,6 +1,7 @@
export type ServerAuthorization = 'basic' | 'none';
export type ServerLocation = 'local' | 'remote' | 'bundled';
export type ServerStatus = 'stopped' | 'starting' | 'running';
export type ServerProtocol = 'http:' | 'https:'
export class Server {
id: number;
@ -14,4 +15,5 @@ export class Server {
login: string;
password: string;
status: ServerStatus;
protocol: ServerProtocol;
}

View File

@ -19,7 +19,7 @@ export class ApplianceService {
}
getUploadPath(server: Server, emulator: string, filename: string) {
return `http://${server.host}:${server.port}/v2/compute/${emulator}/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/compute/${emulator}/images/${filename}`;
}
updateAppliances(server: Server): Observable<Appliance[]> {

View File

@ -6,8 +6,8 @@ export class BuiltInTemplatesConfigurationService {
let categories = [["Default", "guest"],
["Routers", "router"],
["Switches", "switch"],
["End devices", "end_device"],
["Security devices", "security_device"]];
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}
@ -20,8 +20,8 @@ export class BuiltInTemplatesConfigurationService {
let categories = [["Default", "guest"],
["Routers", "router"],
["Switches", "switch"],
["End devices", "end_device"],
["Security devices", "security_device"]];
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}
@ -30,8 +30,8 @@ export class BuiltInTemplatesConfigurationService {
let categories = [["Default", "guest"],
["Routers", "router"],
["Switches", "switch"],
["End devices", "end_device"],
["Security devices", "security_device"]];
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -14,7 +14,7 @@ export class ComputeService {
}
getUploadPath(server: Server, emulator: string, filename: string) {
return `http://${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
}
getStatistics(server: Server): Observable<ComputeStatistics[]> {

View File

@ -8,10 +8,10 @@ export class DockerConfigurationService {
getCategories() {
let categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
["Routers", "router"],
["Switches", "switch"],
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -4,7 +4,7 @@ import { HttpHeaders, HttpClient, HttpParams, HttpErrorResponse } from '@angular
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Server } from '../models/server';
import { Server, ServerProtocol } from '../models/server';
/* tslint:disable:interface-over-type-literal */
export type JsonOptions = {
@ -177,13 +177,10 @@ export class HttpServer {
private getOptionsForServer<T extends HeadersOptions>(server: Server, url: string, options: T) {
if (server.host && server.port) {
if (server.authorization === 'basic') {
url = `https://${server.host}:${server.port}/v2${url}`;
console.log(url);
} else {
url = `http://${server.host}:${server.port}/v2${url}`;
console.log(url);
if (!server.protocol) {
server.protocol = location.protocol as ServerProtocol;
}
url = `${server.protocol}//${server.host}:${server.port}/v2${url}`;
} else {
url = `/v2${url}`;
}

View File

@ -14,7 +14,7 @@ export class IosService {
}
getImagePath(server: Server, filename: string): string {
return `http://${server.host}:${server.port}/v2/compute/dynamips/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/compute/dynamips/images/${filename}`;
}
getTemplates(server: Server): Observable<IosTemplate[]> {

View File

@ -10,8 +10,8 @@ export class IouConfigurationService {
let categories = [["Default", "guest"],
["Routers", "router"],
["Switches", "switch"],
["End devices", "end_device"],
["Security devices", "security_device"]];
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -22,7 +22,7 @@ export class IouService {
}
getImagePath(server: Server, filename: string): string {
return `http://${server.host}:${server.port}/v2/compute/iou/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/compute/iou/images/${filename}`;
}
addTemplate(server: Server, iouTemplate: any): Observable<any> {

View File

@ -1,7 +1,9 @@
import { Injectable, EventEmitter } from "@angular/core";
import { Subject } from 'rxjs';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class MapSettingsService {
public isScrollDisabled = new Subject<boolean>();
public isMapLocked = new Subject<boolean>();

View File

@ -77,11 +77,11 @@ export class ProjectService {
}
getUploadPath(server: Server, uuid: string, project_name: string) {
return `http://${server.host}:${server.port}/v2/projects/${uuid}/import?name=${project_name}`;
return `${server.protocol}//${server.host}:${server.port}/v2/projects/${uuid}/import?name=${project_name}`;
}
getExportPath(server: Server, project: Project) {
return `http://${server.host}:${server.port}/v2/projects/${project.project_id}/export`;
return `${server.protocol}//${server.host}:${server.port}/v2/projects/${project.project_id}/export`;
}
export(server: Server, project_id: string): Observable<any> {

View File

@ -82,10 +82,10 @@ export class QemuConfigurationService {
getCategories() {
let categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
["Routers", "router"],
["Switches", "switch"],
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -20,7 +20,7 @@ export class QemuService {
}
getImagePath(server: Server, filename: string): string {
return `http://${server.host}:${server.port}/v2/compute/qemu/images/${filename}`;
return `${server.protocol}//${server.host}:${server.port}/v2/compute/qemu/images/${filename}`;
}
getBinaries(server: Server): Observable<QemuBinary[]> {

View File

@ -150,6 +150,7 @@ describe('ServerService', () => {
expectedServer.host = 'hostname';
expectedServer.port = 9999;
expectedServer.location = 'bundled';
expectedServer.protocol = 'http:';
service.getLocalServer('hostname', 9999).then(() => {
expect(service.create).toHaveBeenCalledWith(expectedServer);

View File

@ -1,6 +1,6 @@
import { Injectable, EventEmitter } from '@angular/core';
import { IndexedDbService } from './indexed-db.service';
import { Server } from '../models/server';
import { Server, ServerProtocol } from '../models/server';
import { Observable, Subject } from 'rxjs';
import { HttpServer } from './http-server.service';
@ -138,7 +138,7 @@ export class ServerService {
}
public getServerUrl(server: Server) {
return `http://${server.host}:${server.port}/`;
return `${server.protocol}//${server.host}:${server.port}/`;
}
public checkServerVersion(server: Server): Observable<any> {
@ -152,6 +152,7 @@ export class ServerService {
if (local) {
local.host = host;
local.port = port;
local.protocol = location.protocol as ServerProtocol;
this.update(local).then(updated => {
resolve(updated);
}, reject);
@ -161,6 +162,7 @@ export class ServerService {
server.host = host;
server.port = port;
server.location = 'bundled';
server.protocol = location.protocol as ServerProtocol;
this.create(server).then(created => {
resolve(created);
}, reject);

View File

@ -9,7 +9,9 @@ export interface Settings {
console_command: string;
}
@Injectable()
@Injectable({
providedIn: 'root'
})
export class SettingsService {
static DEFAULTS: Settings = {
crash_reports: true,

View File

@ -5,5 +5,6 @@ export function getTestServer(): Server {
server.host = '127.0.0.1';
server.port = 3080;
server.authorization = 'none';
server.protocol = 'http:';
return server;
}

View File

@ -16,10 +16,10 @@ export class VirtualBoxConfigurationService{
getCategories() {
let categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
["Routers", "router"],
["Switches", "switch"],
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -16,10 +16,10 @@ export class VmwareConfigurationService{
getCategories() {
let categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
["Routers", "router"],
["Switches", "switch"],
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

View File

@ -8,10 +8,10 @@ export class VpcsConfigurationService {
getCategories(){
let categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
["Routers", "router"],
["Switches", "switch"],
["End devices", "guest"],
["Security devices", "firewall"]];
return categories;
}

4368
yarn.lock

File diff suppressed because it is too large Load Diff