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 # 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) [![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/) [![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) [![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 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) [![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 ## Demo

View File

@ -1,252 +1,253 @@
{ {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"gns3-web-ui": { "gns3-web-ui": {
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"projectType": "application", "projectType": "application",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"rxjs", "rxjs",
"rxjs-compat", "rxjs-compat",
"uuid", "uuid",
"css-tree", "css-tree",
"save-svg-as-png", "save-svg-as-png",
"angular-draggable-droppable", "angular-draggable-droppable",
"angular2-hotkeys", "angular2-hotkeys",
"dom-set", "dom-set",
"dom-plane", "dom-plane",
"mousetrap", "mousetrap",
"@mattlewis92/dom-autoscroller", "@mattlewis92/dom-autoscroller",
"rxjs/Rx", "rxjs/Rx",
"rxjs/add/operator/map", "rxjs/add/operator/map",
"rxjs-compat/add/operator/map" "rxjs-compat/add/operator/map"
], ],
"aot": true, "aot": true,
"outputPath": "dist", "outputPath": "dist",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json", "tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"extractCss": true, "assets": [
"assets": [ "src/assets",
"src/assets", "src/favicon.ico",
"src/favicon.ico", "src/ReleaseNotes.txt"
"src/ReleaseNotes.txt" ],
], "styles": [
"styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/notosans-fontface/css/notosans-fontface.min.css",
"node_modules/notosans-fontface/css/notosans-fontface.min.css", "src/styles.scss",
"src/styles.css", {
{ "inject": true,
"inject": false, "input": "src/theme.scss",
"input": "src/theme.scss", "bundleName": "theme-default-dark"
"bundleName": "theme-default-dark" },
}, {
{ "inject": true,
"inject": false, "input": "src/theme-light.scss",
"input": "src/theme-light.scss", "bundleName": "theme-default"
"bundleName": "theme-default" }
} ],
], "scripts": []
"scripts": [] },
}, "configurations": {
"configurations": { "production": {
"production": { "budgets": [
"budgets": [ {
{ "type": "anyComponentStyle",
"type": "anyComponentStyle", "maximumWarning": "6kb"
"maximumWarning": "6kb" }
} ],
], "optimization": true,
"optimization": true, "outputHashing": "all",
"outputHashing": "all", "sourceMap": {
"sourceMap": { "hidden": true,
"hidden": true, "scripts": true,
"scripts": true, "styles": false
"styles": false },
}, "namedChunks": false,
"extractCss": true, "aot": true,
"namedChunks": false, "extractLicenses": true,
"aot": true, "vendorChunk": false,
"extractLicenses": true, "buildOptimizer": true,
"vendorChunk": false, "fileReplacements": [
"buildOptimizer": true, {
"fileReplacements": [ "replace": "src/environments/environment.ts",
{ "with": "src/environments/environment.prod.ts"
"replace": "src/environments/environment.ts", }
"with": "src/environments/environment.prod.ts" ]
} },
] "electronProd": {
}, "budgets": [
"electronProd": { {
"budgets": [ "type": "anyComponentStyle",
{ "maximumWarning": "6kb"
"type": "anyComponentStyle", }
"maximumWarning": "6kb" ],
} "optimization": true,
], "outputHashing": "all",
"optimization": true, "sourceMap": false,
"outputHashing": "all", "namedChunks": false,
"sourceMap": false, "aot": true,
"extractCss": true, "extractLicenses": true,
"namedChunks": false, "vendorChunk": false,
"aot": true, "buildOptimizer": true,
"extractLicenses": true, "fileReplacements": [
"vendorChunk": false, {
"buildOptimizer": true, "replace": "src/environments/environment.ts",
"fileReplacements": [ "with": "src/environments/environment.electron.prod.ts"
{ }
"replace": "src/environments/environment.ts", ]
"with": "src/environments/environment.electron.prod.ts" },
} "electronDev": {
] "budgets": [
}, {
"electronDev": { "type": "anyComponentStyle",
"budgets": [ "maximumWarning": "6kb"
{ }
"type": "anyComponentStyle", ],
"maximumWarning": "6kb" "fileReplacements": [
} {
], "replace": "src/environments/environment.ts",
"fileReplacements": [ "with": "src/environments/environment.electron.ts"
{ }
"replace": "src/environments/environment.ts", ]
"with": "src/environments/environment.electron.ts" },
} "githubProd": {
] "budgets": [
}, {
"githubProd": { "type": "anyComponentStyle",
"budgets": [ "maximumWarning": "6kb"
{ }
"type": "anyComponentStyle", ],
"maximumWarning": "6kb" "optimization": true,
} "outputHashing": "all",
], "sourceMap": false,
"optimization": true, "namedChunks": false,
"outputHashing": "all", "aot": true,
"sourceMap": false, "extractLicenses": true,
"extractCss": true, "vendorChunk": false,
"namedChunks": false, "buildOptimizer": true,
"aot": true, "fileReplacements": [
"extractLicenses": true, {
"vendorChunk": false, "replace": "src/environments/environment.ts",
"buildOptimizer": true, "with": "src/environments/environment.github.prod.ts"
"fileReplacements": [ }
{ ]
"replace": "src/environments/environment.ts", }
"with": "src/environments/environment.github.prod.ts" }
} },
] "serve": {
} "builder": "@angular-devkit/build-angular:dev-server",
} "options": {
}, "browserTarget": "gns3-web-ui:build"
"serve": { },
"builder": "@angular-devkit/build-angular:dev-server", "configurations": {
"options": { "production": {
"browserTarget": "gns3-web-ui:build" "browserTarget": "gns3-web-ui:build:production"
}, },
"configurations": { "electronProd": {
"production": { "browserTarget": "gns3-web-ui:build:electronProd"
"browserTarget": "gns3-web-ui:build:production" },
}, "electronDev": {
"electronProd": { "browserTarget": "gns3-web-ui:build:electronDev"
"browserTarget": "gns3-web-ui:build:electronProd" },
}, "githubProd": {
"electronDev": { "browserTarget": "gns3-web-ui:build:githubProd"
"browserTarget": "gns3-web-ui:build:electronDev" }
}, }
"githubProd": { },
"browserTarget": "gns3-web-ui:build:githubProd" "extract-i18n": {
} "builder": "@angular-devkit/build-angular:extract-i18n",
} "options": {
}, "browserTarget": "gns3-web-ui:build"
"extract-i18n": { }
"builder": "@angular-devkit/build-angular:extract-i18n", },
"options": { "test": {
"browserTarget": "gns3-web-ui:build" "builder": "@angular-devkit/build-angular:karma",
} "options": {
}, "main": "src/test.ts",
"test": { "karmaConfig": "./karma.conf.js",
"builder": "@angular-devkit/build-angular:karma", "polyfills": "src/polyfills.ts",
"options": { "tsConfig": "src/tsconfig.spec.json",
"main": "src/test.ts", "scripts": [],
"karmaConfig": "./karma.conf.js", "styles": [
"polyfills": "src/polyfills.ts", "node_modules/bootstrap/dist/css/bootstrap.min.css",
"tsConfig": "src/tsconfig.spec.json", "node_modules/notosans-fontface/css/notosans-fontface.min.css",
"scripts": [], "src/styles.scss",
"styles": [ "src/theme.scss"
"node_modules/bootstrap/dist/css/bootstrap.min.css", ],
"node_modules/notosans-fontface/css/notosans-fontface.min.css", "assets": [
"src/styles.css", "src/assets",
"src/theme.scss" "src/favicon.ico"
], ],
"assets": [ "codeCoverageExclude": [
"src/assets", "src/app/cartography/components/experimental-map/**/*"
"src/favicon.ico" ]
], }
"codeCoverageExclude": [ },
"src/app/cartography/components/experimental-map/**/*" "lint": {
] "builder": "@angular-devkit/build-angular:tslint",
} "options": {
}, "tsConfig": [
"lint": { "src/tsconfig.app.json",
"builder": "@angular-devkit/build-angular:tslint", "src/tsconfig.spec.json"
"options": { ],
"tsConfig": [ "exclude": [
"src/tsconfig.app.json", "**/node_modules/**",
"src/tsconfig.spec.json" "**/*.spec.ts"
], ]
"exclude": [ }
"**/node_modules/**", }
"**/*.spec.ts" },
] "schematics": {
} "@schematics/angular:component": {
} "style": "scss"
} }
}, }
"gns3-web-ui-e2e": { },
"root": "e2e", "gns3-web-ui-e2e": {
"sourceRoot": "e2e", "root": "e2e",
"projectType": "application", "sourceRoot": "e2e",
"architect": { "projectType": "application",
"e2e": { "architect": {
"builder": "@angular-devkit/build-angular:protractor", "e2e": {
"options": { "builder": "@angular-devkit/build-angular:protractor",
"protractorConfig": "./protractor.conf.js", "options": {
"devServerTarget": "gns3-web-ui:serve" "protractorConfig": "./protractor.conf.js",
} "devServerTarget": "gns3-web-ui:serve"
}, }
"lint": { },
"builder": "@angular-devkit/build-angular:tslint", "lint": {
"options": { "builder": "@angular-devkit/build-angular:tslint",
"tsConfig": [ "options": {
"e2e/tsconfig.e2e.json" "tsConfig": [
], "e2e/tsconfig.e2e.json"
"exclude": [ ],
"**/node_modules/**" "exclude": [
] "**/node_modules/**"
} ]
} }
} }
} }
}, }
"defaultProject": "gns3-web-ui", },
"schematics": { "defaultProject": "gns3-web-ui",
"@schematics/angular:component": { "schematics": {
"prefix": "app", "@schematics/angular:component": {
"style": "scss" "prefix": "app",
}, "style": "scss"
"@schematics/angular:directive": { },
"prefix": "app" "@schematics/angular:directive": {
} "prefix": "app"
}, }
"cli": { },
"analytics": false "cli": {
} "analytics": false
}
} }

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

View File

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

View File

@ -7,7 +7,6 @@ import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { D3Service } from 'd3-ng2-service'; import { D3Service } from 'd3-ng2-service';
import { HotkeyModule } from 'angular2-hotkeys';
import { PersistenceModule } from 'angular-persistence'; import { PersistenceModule } from 'angular-persistence';
import { NgxElectronModule } from 'ngx-electron'; import { NgxElectronModule } from 'ngx-electron';
import { FileUploadModule } from 'ng2-file-upload'; 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 { 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 { 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 { 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 { SettingsComponent } from './components/settings/settings.component';
import { SettingsService } from './services/settings.service'; import { SettingsService } from './services/settings.service';
@ -321,7 +319,6 @@ import { ProjectReadmeComponent } from './components/project-map/project-readme/
ResumeLinkActionComponent, ResumeLinkActionComponent,
SuspendLinkActionComponent, SuspendLinkActionComponent,
ResetLinkActionComponent, ResetLinkActionComponent,
ProjectMapShortcutsComponent,
SettingsComponent, SettingsComponent,
PreferencesComponent, PreferencesComponent,
BundledServerFinderComponent, BundledServerFinderComponent,
@ -489,7 +486,6 @@ import { ProjectReadmeComponent } from './components/project-map/project-readme/
BrowserAnimationsModule, BrowserAnimationsModule,
CdkTableModule, CdkTableModule,
CartographyModule, CartographyModule,
HotkeyModule.forRoot(),
PersistenceModule, PersistenceModule,
NgxElectronModule, NgxElectronModule,
FileUploadModule, FileUploadModule,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -78,6 +78,6 @@ export class SymbolsComponent implements OnInit {
} }
getImageSourceForTemplate(symbol: string) { 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 { .consoleWrapper {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
position: fixed; 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 { 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'; import { ConsoleDeviceActionBrowserComponent } from '../context-menu/actions/console-device-action-browser/console-device-action-browser.component';
fdescribe('ContextConsoleMenuComponent', () => { describe('ContextConsoleMenuComponent', () => {
let component: ContextConsoleMenuComponent; let component: ContextConsoleMenuComponent;
let fixture: ComponentFixture<ContextConsoleMenuComponent>; let fixture: ComponentFixture<ContextConsoleMenuComponent>;
let toasterService: MockedToasterService = new MockedToasterService(); let toasterService: MockedToasterService = new MockedToasterService();

View File

@ -154,6 +154,6 @@ export class ImportApplianceComponent implements OnInit {
} }
private getUploadPath(server: Server, emulator: string, filename: string) { 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(); 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', () => { it('should call show message when unknown command entered', () => {

View File

@ -167,7 +167,7 @@
type="file" type="file"
class="non-visible" class="non-visible"
#file2 #file2
(change)="importImage($event)" (change)="importImage($event, version.images.hda_disk_image)"
ng2FileSelect ng2FileSelect
[uploader]="uploaderImage"/> [uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button> <button class="button" mat-raised-button (click)="file2.click()">Import</button>
@ -187,11 +187,11 @@
<input <input
type="file" type="file"
class="non-visible" class="non-visible"
#file2 #file3
(change)="importImage($event)" (change)="importImage($event, version.images.hdb_disk_image)"
ng2FileSelect ng2FileSelect
[uploader]="uploaderImage"/> [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> <button class="button" mat-raised-button (click)="downloadImageFromVersion(version.images.hdb_disk_image)">Download</button>
</div> </div>
</div> </div>
@ -235,7 +235,7 @@
type="file" type="file"
class="non-visible" class="non-visible"
#file2 #file2
(change)="importImage($event)" (change)="importImage($event, image.filename)"
ng2FileSelect ng2FileSelect
[uploader]="uploaderImage"/> [uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button> <button class="button" mat-raised-button (click)="file2.click()">Import</button>
@ -271,7 +271,7 @@
type="file" type="file"
class="non-visible" class="non-visible"
#file2 #file2
(change)="importImage($event)" (change)="importImage($, image.filename)"
ng2FileSelect ng2FileSelect
[uploader]="uploaderImage"/> [uploader]="uploaderImage"/>
<button class="button" mat-raised-button (click)="file2.click()">Import</button> <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 { InformationDialogComponent } from '../../../components/dialogs/information-dialog.component';
import { ProgressService } from '../../../common/progress/progress.service'; import { ProgressService } from '../../../common/progress/progress.service';
import { TemplateNameDialogComponent } from './template-name-dialog/template-name-dialog.component'; import { TemplateNameDialogComponent } from './template-name-dialog/template-name-dialog.component';
import * as SparkMD5 from 'spark-md5';
@Component({ @Component({
selector: 'app-new-template-dialog', 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.uploaderImage.onErrorItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.error('An error has occured'); this.toasterService.error('An error has occured');
this.progressService.deactivate(); this.progressService.deactivate();
this.uploaderImage.clearQueue();
}; };
this.uploaderImage.onSuccessItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => { this.uploaderImage.onSuccessItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.success('Image imported succesfully'); this.toasterService.success('Image imported succesfully');
this.refreshImages(); this.refreshImages();
this.progressService.deactivate(); this.progressService.deactivate();
this.uploaderImage.clearQueue();
}; };
} }
@ -332,7 +335,35 @@ export class NewTemplateDialogComponent implements OnInit {
dialogRef.componentInstance.appliance = object; 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 name = event.target.files[0].name.split('-')[0];
let fileName = event.target.files[0].name; let fileName = event.target.files[0].name;
let file = event.target.files[0]; let file = event.target.files[0];
@ -373,10 +404,10 @@ export class NewTemplateDialogComponent implements OnInit {
checkImages(version: Version): boolean { checkImages(version: Version): boolean {
if (version.images.hdb_disk_image) { if (version.images.hdb_disk_image) {
if (this.checkImageFromVersion(version.images.hda_disk_image) && this.checkImageFromVersion(version.images.hdb_disk_image)) return true; 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; if (this.checkImageFromVersion(version.images.hda_disk_image)) return true;
return false; 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) { 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> </div>
<app-progress></app-progress> <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-draw-link-tool [links]="links" *ngIf="tools.draw_link"></app-draw-link-tool>
<app-drawing-dragged [server]="server" [project]="project"></app-drawing-dragged> <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 { NewTemplateDialogComponent } from './new-template-dialog/new-template-dialog.component';
import { NodeConsoleService } from '../../services/nodeConsole.service'; import { NodeConsoleService } from '../../services/nodeConsole.service';
import { ProjectReadmeComponent } from './project-readme/project-readme.component'; import { ProjectReadmeComponent } from './project-readme/project-readme.component';
import * as Mousetrap from 'mousetrap';
@Component({ @Component({
@ -168,12 +169,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
) {} ) {}
ngOnInit() { ngOnInit() {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false; this.getSettings();
this.settings = this.settingsService.getAll();
this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible;
this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible;
this.mapSettingsService.logConsoleSubject.subscribe(value => this.isConsoleVisible = value);
this.progressService.activate(); this.progressService.activate();
if (this.serverService.isServiceInitialized) { 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.projectMapSubscription.add(
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => { this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
if (this.scrollEnabled) this.centerCanvas(); if (this.scrollEnabled) this.centerCanvas();
@ -203,7 +215,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.nodesDataSource.changes.subscribe((nodes: Node[]) => { this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
if (!this.server) return; if (!this.server) return;
nodes.forEach((node: Node) => { 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; this.nodes = nodes;
@ -231,11 +243,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
message: message 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() { getData() {
@ -330,6 +337,20 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
event.preventDefault(); event.preventDefault();
this.router.navigate(['/server', this.server.id, 'preferences']); 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) { onProjectLoad(project: Project) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,12 @@
<input matInput tabindex="1" formControlName="port" placeholder="Port" /> <input matInput tabindex="1" formControlName="port" placeholder="Port" />
</mat-form-field> </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-form-field *ngIf="serverForm.get('location').value === 'remote'">
<mat-select placeholder="Authorization" formControlName="authorization" > <mat-select placeholder="Authorization" formControlName="authorization" >
<mat-option *ngFor="let auth of authorizations" [value]="auth.key"> {{ auth.name }} </mat-option> <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 { export class AddServerDialogComponent implements OnInit {
authorizations = [{ key: 'none', name: 'No authorization' }, { key: 'basic', name: 'Basic authorization' }]; authorizations = [{ key: 'none', name: 'No authorization' }, { key: 'basic', name: 'Basic authorization' }];
protocols = [{ key: 'http:', name: 'HTTP' }, { key: 'https:', name: 'HTTPS' }];
locations = []; locations = [];
serverForm = new FormGroup({ serverForm = new FormGroup({
@ -22,6 +23,7 @@ export class AddServerDialogComponent implements OnInit {
'ubridge_path': new FormControl(''), 'ubridge_path': new FormControl(''),
'host': new FormControl('', [ Validators.required ]), 'host': new FormControl('', [ Validators.required ]),
'port': new FormControl('', [ Validators.required, Validators.min(1) ]), 'port': new FormControl('', [ Validators.required, Validators.min(1) ]),
'protocol': new FormControl('http:'),
'authorization': new FormControl('none'), 'authorization': new FormControl('none'),
'login': new FormControl(''), 'login': new FormControl(''),
'password': new FormControl('') 'password': new FormControl('')

View File

@ -3,13 +3,14 @@ import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { map } from 'rxjs//operators'; import { map } from 'rxjs//operators';
import { Server } from '../../../models/server'; import { Server, ServerProtocol } from '../../../models/server';
import { VersionService } from '../../../services/version.service'; import { VersionService } from '../../../services/version.service';
import { Version } from '../../../models/version'; import { Version } from '../../../models/version';
import { forkJoin } from 'rxjs'; import { forkJoin } from 'rxjs';
import { ServerService } from '../../../services/server.service'; import { ServerService } from '../../../services/server.service';
import { ServerDatabase } from '../../../services/server.database'; import { ServerDatabase } from '../../../services/server.database';
import { from } from 'rxjs'; import { from } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
@Component({ @Component({
selector: 'app-server-discovery', selector: 'app-server-discovery',
@ -29,33 +30,62 @@ export class ServerDiscoveryComponent implements OnInit {
constructor( constructor(
private versionService: VersionService, private versionService: VersionService,
private serverService: ServerService, private serverService: ServerService,
private serverDatabase: ServerDatabase private serverDatabase: ServerDatabase,
private route : ActivatedRoute
) {} ) {}
ngOnInit() { ngOnInit() {
if (this.serverService.isServiceInitialized) this.discoverFirstAvailableServer(); if (this.serverService.isServiceInitialized) this.discoverFirstServer();
this.serverService.serviceInitialized.subscribe(async (value: boolean) => { this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
if (value) { 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() { discoverFirstAvailableServer() {
forkJoin( forkJoin(
[from(this.serverService.findAll()).pipe(map((s: Server[]) => s)), [from(this.serverService.findAll()).pipe(map((s: Server[]) => s)),
this.discovery()] this.discovery()]
).subscribe(([local, discovered]) => { ).subscribe(
local.forEach(added => { ([local, discovered]) => {
discovered = discovered.filter(server => { local.forEach(added => {
return !(server.host == added.host && server.port == added.port); discovered = discovered.filter(server => {
return !(server.host == added.host && server.port == added.port);
});
}); });
}); if (discovered.length > 0) {
if (discovered.length > 0) { this.discoveredServer = discovered.shift();
this.discoveredServer = discovered.shift(); }
} },
}); error => {});
} }
discovery(): Observable<Server[]> { discovery(): Observable<Server[]> {
@ -94,6 +124,7 @@ export class ServerDiscoveryComponent implements OnInit {
} }
server.location = 'remote'; server.location = 'remote';
server.protocol = location.protocol as ServerProtocol;
this.serverService.create(server).then((created: Server) => { this.serverService.create(server).then((created: Server) => {
this.serverDatabase.addServer(created); 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 { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Observable, merge, Subscription } from 'rxjs'; import { Observable, merge, Subscription } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Server } from '../../models/server'; import { Server, ServerProtocol } from '../../models/server';
import { ServerService } from '../../services/server.service'; import { ServerService } from '../../services/server.service';
import { ServerDatabase } from '../../services/server.database'; import { ServerDatabase } from '../../services/server.database';
import { AddServerDialogComponent } from './add-server-dialog/add-server-dialog.component'; import { AddServerDialogComponent } from './add-server-dialog/add-server-dialog.component';
@ -18,7 +18,7 @@ import { ConfirmationBottomSheetComponent } from '../projects/confirmation-botto
@Component({ @Component({
selector: 'app-server-list', selector: 'app-server-list',
templateUrl: './servers.component.html', templateUrl: './servers.component.html',
styleUrls: ['./servers.component.css'] styleUrls: ['./servers.component.scss']
}) })
export class ServersComponent implements OnInit, OnDestroy { export class ServersComponent implements OnInit, OnDestroy {
dataSource: ServerDataSource; dataSource: ServerDataSource;
@ -35,6 +35,7 @@ export class ServersComponent implements OnInit, OnDestroy {
private electronService: ElectronService, private electronService: ElectronService,
private childProcessService: ChildProcessService, private childProcessService: ChildProcessService,
private bottomSheet: MatBottomSheet, private bottomSheet: MatBottomSheet,
private route : ActivatedRoute
) {} ) {}
getServers() { getServers() {
@ -52,6 +53,7 @@ export class ServersComponent implements OnInit, OnDestroy {
this.serverService.checkServerVersion(server).subscribe( this.serverService.checkServerVersion(server).subscribe(
(serverInfo) => { (serverInfo) => {
if ((serverInfo.version.split('.')[1]>=2) && (serverInfo.version.split('.')[0]>=2)) { 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); 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 { MockedToasterService } from '../../services/toaster.service.spec';
import { ConsoleService } from '../../services/settings/console.service'; import { ConsoleService } from '../../services/settings/console.service';
import { MapSettingsService } from '../../services/mapsettings.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', () => { describe('SettingsComponent', () => {
let component: SettingsComponent; let component: SettingsComponent;
@ -25,6 +27,7 @@ describe('SettingsComponent', () => {
toggleIntegrateInterfaceLabels(val: boolean) {} toggleIntegrateInterfaceLabels(val: boolean) {}
}; };
let consoleService; let consoleService;
let updatesService = autoSpy(UpdatesService);
beforeEach(async(() => { beforeEach(async(() => {
consoleService = { consoleService = {
@ -37,7 +40,8 @@ describe('SettingsComponent', () => {
SettingsService, SettingsService,
{ provide: ToasterService, useClass: MockedToasterService }, { provide: ToasterService, useClass: MockedToasterService },
{ provide: ConsoleService, useValue: consoleService }, { provide: ConsoleService, useValue: consoleService },
{ provide: MapSettingsService, useValue: mapSettingsService } { provide: MapSettingsService, useValue: mapSettingsService },
{ provide: UpdatesService, useValue: updatesService }
], ],
declarations: [SettingsComponent] declarations: [SettingsComponent]
}).compileComponents(); }).compileComponents();

View File

@ -116,7 +116,7 @@ export class TemplateComponent implements OnInit, OnDestroy {
} }
getImageSourceForTemplate(template: Template) { 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() { ngOnDestroy() {

View File

@ -72,6 +72,12 @@
<div class="summaryContentServers"> <div class="summaryContentServers">
<div class="nodeRow" *ngFor="let compute of computes"> <div class="nodeRow" *ngFor="let compute of computes">
<div> <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}} {{compute.name}}
</div> </div>
<div> <div>

View File

@ -1,3 +1,9 @@
@media screen and (max-width: 600px) {
.summaryWrapper {
visibility: hidden;
}
}
.summaryWrapper { .summaryWrapper {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
position: fixed; 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', selector: 'app-default-layout',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
templateUrl: './default-layout.component.html', templateUrl: './default-layout.component.html',
styleUrls: ['./default-layout.component.css'] styleUrls: ['./default-layout.component.scss']
}) })
export class DefaultLayoutComponent implements OnInit, OnDestroy { export class DefaultLayoutComponent implements OnInit, OnDestroy {
public isInstalledSoftwareAvailable = false; public isInstalledSoftwareAvailable = false;

View File

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

View File

@ -19,7 +19,7 @@ export class ApplianceService {
} }
getUploadPath(server: Server, emulator: string, filename: string) { 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[]> { updateAppliances(server: Server): Observable<Appliance[]> {

View File

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

View File

@ -14,7 +14,7 @@ export class ComputeService {
} }
getUploadPath(server: Server, emulator: string, filename: string) { 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[]> { getStatistics(server: Server): Observable<ComputeStatistics[]> {

View File

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

View File

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

View File

@ -14,7 +14,7 @@ export class IosService {
} }
getImagePath(server: Server, filename: string): string { 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[]> { getTemplates(server: Server): Observable<IosTemplate[]> {

View File

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

View File

@ -22,7 +22,7 @@ export class IouService {
} }
getImagePath(server: Server, filename: string): string { 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> { addTemplate(server: Server, iouTemplate: any): Observable<any> {

View File

@ -1,7 +1,9 @@
import { Injectable, EventEmitter } from "@angular/core"; import { Injectable, EventEmitter } from "@angular/core";
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@Injectable() @Injectable({
providedIn: 'root'
})
export class MapSettingsService { export class MapSettingsService {
public isScrollDisabled = new Subject<boolean>(); public isScrollDisabled = new Subject<boolean>();
public isMapLocked = 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) { 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) { 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> { export(server: Server, project_id: string): Observable<any> {

View File

@ -64,17 +64,17 @@ export class QemuConfigurationService {
getBootPriorities() { getBootPriorities() {
let bootPriorities = [["HDD", "c"], let bootPriorities = [["HDD", "c"],
["CD/DVD-ROM", "d"], ["CD/DVD-ROM", "d"],
["Network", "n"], ["Network", "n"],
["HDD or Network", "cn"], ["HDD or Network", "cn"],
["HDD or CD/DVD-ROM", "cd"]]; ["HDD or CD/DVD-ROM", "cd"]];
return bootPriorities; return bootPriorities;
} }
getOnCloseOptions() { getOnCloseOptions() {
let onCloseOptions = [["Power off the VM", "power_off"], let onCloseOptions = [["Power off the VM", "power_off"],
["Send the shutdown signal (ACPI)", "shutdown_signal"], ["Send the shutdown signal (ACPI)", "shutdown_signal"],
["Save the VM state", "save_vm_state"]]; ["Save the VM state", "save_vm_state"]];
return onCloseOptions; return onCloseOptions;
@ -82,10 +82,10 @@ export class QemuConfigurationService {
getCategories() { getCategories() {
let categories = [["Default", "guest"], let categories = [["Default", "guest"],
["Routers", "routers"], ["Routers", "router"],
["Switches", "switches"], ["Switches", "switch"],
["End devices", "end_devices"], ["End devices", "guest"],
["Security devices", "security_devices"]]; ["Security devices", "firewall"]];
return categories; return categories;
} }

View File

@ -20,7 +20,7 @@ export class QemuService {
} }
getImagePath(server: Server, filename: string): string { 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[]> { getBinaries(server: Server): Observable<QemuBinary[]> {

View File

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

View File

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

View File

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

View File

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

View File

@ -7,8 +7,8 @@ export class VirtualBoxConfigurationService{
} }
getOnCloseoptions() { getOnCloseoptions() {
let onCloseOptions = [["Power off the VM", "power_off"], let onCloseOptions = [["Power off the VM", "power_off"],
["Send the shutdown signal (ACPI)", "shutdown_signal"], ["Send the shutdown signal (ACPI)", "shutdown_signal"],
["Save the VM state", "save_vm_state"]]; ["Save the VM state", "save_vm_state"]];
return onCloseOptions; return onCloseOptions;
@ -16,10 +16,10 @@ export class VirtualBoxConfigurationService{
getCategories() { getCategories() {
let categories = [["Default", "guest"], let categories = [["Default", "guest"],
["Routers", "routers"], ["Routers", "router"],
["Switches", "switches"], ["Switches", "switch"],
["End devices", "end_devices"], ["End devices", "guest"],
["Security devices", "security_devices"]]; ["Security devices", "firewall"]];
return categories; return categories;
} }
@ -33,5 +33,5 @@ export class VirtualBoxConfigurationService{
"Paravirtualized Network (virtio-net)"]; "Paravirtualized Network (virtio-net)"];
return networkTypes; return networkTypes;
} }
} }

View File

@ -7,8 +7,8 @@ export class VmwareConfigurationService{
} }
getOnCloseoptions() { getOnCloseoptions() {
let onCloseOptions = [["Power off the VM", "power_off"], let onCloseOptions = [["Power off the VM", "power_off"],
["Send the shutdown signal (ACPI)", "shutdown_signal"], ["Send the shutdown signal (ACPI)", "shutdown_signal"],
["Save the VM state", "save_vm_state"]]; ["Save the VM state", "save_vm_state"]];
return onCloseOptions; return onCloseOptions;
@ -16,10 +16,10 @@ export class VmwareConfigurationService{
getCategories() { getCategories() {
let categories = [["Default", "guest"], let categories = [["Default", "guest"],
["Routers", "routers"], ["Routers", "router"],
["Switches", "switches"], ["Switches", "switch"],
["End devices", "end_devices"], ["End devices", "guest"],
["Security devices", "security_devices"]]; ["Security devices", "firewall"]];
return categories; return categories;
} }
@ -35,5 +35,5 @@ export class VmwareConfigurationService{
"vmxnet3"]; "vmxnet3"];
return networkTypes; return networkTypes;
} }
} }

View File

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

4368
yarn.lock

File diff suppressed because it is too large Load Diff