mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-02 02:56:42 +00:00
Merge branch 'master-3.0' into enhancement/1354
This commit is contained in:
commit
c3bfad647f
17
.github/workflows/main.yml
vendored
17
.github/workflows/main.yml
vendored
@ -2,7 +2,7 @@ name: Build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '**'
|
- '**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@ -10,14 +10,17 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: [ 14, 16, 18 ]
|
||||||
|
name: Node ${{ matrix.node }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Setup node 14
|
- name: Setup ${{ matrix.node }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: ${{ matrix.node }}
|
||||||
- uses: c-hive/gha-yarn-cache@v1
|
- uses: c-hive/gha-yarn-cache@v1
|
||||||
- name: Install JS dependencies
|
- name: Install JS dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
43
e2e/controllers.e2e-spec.ts
Normal file
43
e2e/controllers.e2e-spec.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { TestHelper } from './helpers/common.po';
|
||||||
|
import { ControllersPage } from './helpers/controller.po';
|
||||||
|
|
||||||
|
describe('Controllers page', () => {
|
||||||
|
let page: ControllersPage;
|
||||||
|
let helper: TestHelper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new ControllersPage();
|
||||||
|
helper = new TestHelper();
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('user should have possibility to add controller', async () => {
|
||||||
|
// arrange
|
||||||
|
page.maximizeWindow();
|
||||||
|
await page.navigateToControllersPage();
|
||||||
|
|
||||||
|
// act
|
||||||
|
let text = await page.getAddControllerNotificationText();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(text).toBe("We've discovered GNS3 controller on 127.0.0.1:3080, would you like to add to the list?");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('user should see added controller in the list', async () => {
|
||||||
|
// arrange
|
||||||
|
page.maximizeWindow();
|
||||||
|
await page.navigateToControllersPage();
|
||||||
|
await page.clickAddController();
|
||||||
|
helper.sleep(1000);
|
||||||
|
|
||||||
|
// act
|
||||||
|
let firstRowOfControllersTable = await page.checkControllersTable();
|
||||||
|
let controllerData = [];
|
||||||
|
await helper.asyncForEach(firstRowOfControllersTable, async (element) => {
|
||||||
|
controllerData.push(await element.getText());
|
||||||
|
});
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(controllerData).toContain('127.0.0.1');
|
||||||
|
expect(controllerData).toContain('3080');
|
||||||
|
});
|
||||||
|
});
|
@ -1,41 +1,41 @@
|
|||||||
import { browser, by } from 'protractor';
|
import { browser, by } from 'protractor';
|
||||||
import { TestHelper } from './common.po';
|
import { TestHelper } from './common.po';
|
||||||
|
|
||||||
export class ServersPage {
|
export class ControllersPage {
|
||||||
helper = new TestHelper();
|
helper = new TestHelper();
|
||||||
|
|
||||||
maximizeWindow() {
|
maximizeWindow() {
|
||||||
browser.driver.manage().window().maximize();
|
browser.driver.manage().window().maximize();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateToServersPage() {
|
navigateToControllersPage() {
|
||||||
return browser.get('/servers');
|
return browser.get('/controllers');
|
||||||
}
|
}
|
||||||
|
|
||||||
getAddServerNotificationText() {
|
getAddControllerNotificationText() {
|
||||||
return browser.driver.findElement(by.className('mat-card-content')).getText();
|
return browser.driver.findElement(by.className('mat-card-content')).getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
async clickAddServer() {
|
async clickAddController() {
|
||||||
let serversTable = await this.checkServersTable();
|
let controllerTable = await this.checkControllersTable();
|
||||||
if (serversTable.length === 0) {
|
if (controllerTable.length === 0) {
|
||||||
let buttons = await browser.driver.findElements(by.className('mat-button mat-button-base'));
|
let buttons = await browser.driver.findElements(by.className('mat-button mat-button-base'));
|
||||||
await buttons[3].click();
|
await buttons[3].click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkServersTable() {
|
checkControllersTable() {
|
||||||
return browser.driver.findElements(by.css('mat-cell'));
|
return browser.driver.findElements(by.css('mat-cell'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async navigateToServerProjects() {
|
async navigateToControllerProjects() {
|
||||||
this.helper.sleep(2000);
|
this.helper.sleep(2000);
|
||||||
let hyperlinks = await browser.driver.findElements(by.css('a.table-link'));
|
let hyperlinks = await browser.driver.findElements(by.css('a.table-link'));
|
||||||
let serverLink;
|
let controllerLink;
|
||||||
await this.helper.asyncForEach(hyperlinks, async (element) => {
|
await this.helper.asyncForEach(hyperlinks, async (element) => {
|
||||||
let text = await element.getText();
|
let text = await element.getText();
|
||||||
if (text === '127.0.0.1') serverLink = element;
|
if (text === '127.0.0.1') controllerLink = element;
|
||||||
});
|
});
|
||||||
await serverLink.click();
|
await controllerLink.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,24 +1,24 @@
|
|||||||
import { TestHelper } from './helpers/common.po';
|
import { TestHelper } from './helpers/common.po';
|
||||||
import { ProjectMapPage } from './helpers/project-map.po';
|
import { ProjectMapPage } from './helpers/project-map.po';
|
||||||
import { ProjectsPage } from './helpers/project.po';
|
import { ProjectsPage } from './helpers/project.po';
|
||||||
import { ServersPage } from './helpers/server.po';
|
import { ControllersPage } from './helpers/controller.po';
|
||||||
|
|
||||||
describe('Project map page', () => {
|
describe('Project map page', () => {
|
||||||
let serversPage: ServersPage;
|
let controllersPage: ControllersPage;
|
||||||
let projectsPage: ProjectsPage;
|
let projectsPage: ProjectsPage;
|
||||||
let projectMapPage: ProjectMapPage;
|
let projectMapPage: ProjectMapPage;
|
||||||
let helper: TestHelper;
|
let helper: TestHelper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
serversPage = new ServersPage();
|
controllersPage = new ControllersPage();
|
||||||
projectsPage = new ProjectsPage();
|
projectsPage = new ProjectsPage();
|
||||||
projectMapPage = new ProjectMapPage();
|
projectMapPage = new ProjectMapPage();
|
||||||
helper = new TestHelper();
|
helper = new TestHelper();
|
||||||
|
|
||||||
serversPage.maximizeWindow();
|
controllersPage.maximizeWindow();
|
||||||
await serversPage.navigateToServersPage();
|
await controllersPage.navigateToControllersPage();
|
||||||
await serversPage.clickAddServer();
|
await controllersPage.clickAddController();
|
||||||
await serversPage.navigateToServerProjects();
|
await controllersPage.navigateToControllerProjects();
|
||||||
await projectsPage.openAddProjectDialog();
|
await projectsPage.openAddProjectDialog();
|
||||||
helper.sleep(2000);
|
helper.sleep(2000);
|
||||||
await projectsPage.createProject();
|
await projectsPage.createProject();
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { TestHelper } from './helpers/common.po';
|
import { TestHelper } from './helpers/common.po';
|
||||||
import { ProjectsPage } from './helpers/project.po';
|
import { ProjectsPage } from './helpers/project.po';
|
||||||
import { ServersPage } from './helpers/server.po';
|
import { ControllersPage } from './helpers/controller.po';
|
||||||
|
|
||||||
describe('Projects page', () => {
|
describe('Projects page', () => {
|
||||||
let serversPage: ServersPage;
|
let controllersPage: ControllersPage;
|
||||||
let projectsPage: ProjectsPage;
|
let projectsPage: ProjectsPage;
|
||||||
let helper: TestHelper;
|
let helper: TestHelper;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
serversPage = new ServersPage();
|
controllersPage = new ControllersPage();
|
||||||
projectsPage = new ProjectsPage();
|
projectsPage = new ProjectsPage();
|
||||||
helper = new TestHelper();
|
helper = new TestHelper();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('user should have possibility to create new project', async () => {
|
it('user should have possibility to create new project', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
serversPage.maximizeWindow();
|
controllersPage.maximizeWindow();
|
||||||
await serversPage.navigateToServersPage();
|
await controllersPage.navigateToControllersPage();
|
||||||
await serversPage.clickAddServer();
|
await controllersPage.clickAddController();
|
||||||
await serversPage.navigateToServerProjects();
|
await controllersPage.navigateToControllerProjects();
|
||||||
helper.sleep(2000);
|
helper.sleep(2000);
|
||||||
|
|
||||||
//act
|
//act
|
||||||
@ -28,6 +28,6 @@ describe('Projects page', () => {
|
|||||||
helper.sleep(2000);
|
helper.sleep(2000);
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
expect(helper.getCurrentUrl()).toMatch('server/1/project/');
|
expect(helper.getCurrentUrl()).toMatch('controller/1/project/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import { TestHelper } from './helpers/common.po';
|
|
||||||
import { ServersPage } from './helpers/server.po';
|
|
||||||
|
|
||||||
describe('Servers page', () => {
|
|
||||||
let page: ServersPage;
|
|
||||||
let helper: TestHelper;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
page = new ServersPage();
|
|
||||||
helper = new TestHelper();
|
|
||||||
});
|
|
||||||
|
|
||||||
xit('user should have possibility to add server', async () => {
|
|
||||||
// arrange
|
|
||||||
page.maximizeWindow();
|
|
||||||
await page.navigateToServersPage();
|
|
||||||
|
|
||||||
// act
|
|
||||||
let text = await page.getAddServerNotificationText();
|
|
||||||
|
|
||||||
// assert
|
|
||||||
expect(text).toBe("We've discovered GNS3 server on 127.0.0.1:3080, would you like to add to the list?");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('user should see added server in the list', async () => {
|
|
||||||
// arrange
|
|
||||||
page.maximizeWindow();
|
|
||||||
await page.navigateToServersPage();
|
|
||||||
await page.clickAddServer();
|
|
||||||
helper.sleep(1000);
|
|
||||||
|
|
||||||
// act
|
|
||||||
let firstRowOfServersTable = await page.checkServersTable();
|
|
||||||
let serverData = [];
|
|
||||||
await helper.asyncForEach(firstRowOfServersTable, async (element) => {
|
|
||||||
serverData.push(await element.getText());
|
|
||||||
});
|
|
||||||
|
|
||||||
// assert
|
|
||||||
expect(serverData).toContain('127.0.0.1');
|
|
||||||
expect(serverData).toContain('3080');
|
|
||||||
});
|
|
||||||
});
|
|
@ -15,7 +15,7 @@ files:
|
|||||||
- renderer.js
|
- renderer.js
|
||||||
- sentry.js
|
- sentry.js
|
||||||
- installed-software.js
|
- installed-software.js
|
||||||
- local-server.js
|
- local-controller.js
|
||||||
- console-executor.js
|
- console-executor.js
|
||||||
- package.json
|
- package.json
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ const { app } = require('electron')
|
|||||||
|
|
||||||
const isWin = /^win/.test(process.platform);
|
const isWin = /^win/.test(process.platform);
|
||||||
|
|
||||||
let runningServers = {};
|
let runningControllers = {};
|
||||||
|
|
||||||
exports.getLocalServerPath = async () => {
|
exports.getLocalControllerPath = async () => {
|
||||||
let binary = isWin ? 'gns3server.exe': 'gns3server';
|
let binary = isWin ? 'gns3server.exe': 'gns3server';
|
||||||
return findBinary('exe.', binary);
|
return findBinary('exe.', binary);
|
||||||
}
|
}
|
||||||
@ -20,27 +20,27 @@ exports.getUbridgePath = async () => {
|
|||||||
return findBinary('ubridge', binary);
|
return findBinary('ubridge', binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.startLocalServer = async (server) => {
|
exports.startLocalController = async (controller) => {
|
||||||
return await run(server, {
|
return await run(controller, {
|
||||||
logStdout: true
|
logStdout: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.stopLocalServer = async (server) => {
|
exports.stopLocalController = async (controller) => {
|
||||||
return await stop(server.name);
|
return await stop(controller.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getRunningServers = () => {
|
exports.getRunningControllers = () => {
|
||||||
return Object.keys(runningServers);
|
return Object.keys(runningControllers);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.stopAllLocalServers = async () => {
|
exports.stopAllLocalControllers = async () => {
|
||||||
return await stopAll();
|
return await stopAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findBinary(binaryDirectory, filename) {
|
async function findBinary(binaryDirectory, filename) {
|
||||||
const lookupDirectories = [
|
const lookupDirectories = [
|
||||||
__dirname,
|
__dirname,
|
||||||
path.dirname(app.getPath('exe'))
|
path.dirname(app.getPath('exe'))
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ async function findBinaryInDirectory(baseDirectory, binaryDirectory, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(distDirectory);
|
const files = fs.readdirSync(distDirectory);
|
||||||
|
|
||||||
let binaryPath = null;
|
let binaryPath = null;
|
||||||
|
|
||||||
files.forEach((directory) => {
|
files.forEach((directory) => {
|
||||||
@ -77,33 +77,33 @@ async function findBinaryInDirectory(baseDirectory, binaryDirectory, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getServerArguments(server, overrides, configPath) {
|
function getControllerArguments(controller, overrides, configPath) {
|
||||||
let serverArguments = [];
|
let controllerArguments = [];
|
||||||
if(server.host) {
|
if(controller.host) {
|
||||||
serverArguments.push('--host');
|
controllerArguments.push('--host');
|
||||||
serverArguments.push(server.host);
|
controllerArguments.push(controller.host);
|
||||||
}
|
}
|
||||||
if(server.port) {
|
if(controller.port) {
|
||||||
serverArguments.push('--port');
|
controllerArguments.push('--port');
|
||||||
serverArguments.push(server.port);
|
controllerArguments.push(controller.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
serverArguments.push('--local');
|
controllerArguments.push('--local');
|
||||||
|
|
||||||
if(configPath) {
|
if(configPath) {
|
||||||
serverArguments.push('--config');
|
controllerArguments.push('--config');
|
||||||
serverArguments.push(configPath);
|
controllerArguments.push(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverArguments;
|
return controllerArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getChannelForServer(server) {
|
function getChannelForController(controller) {
|
||||||
return `local-server-run-${server.name}`;
|
return `local-controller-run-${controller.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyStatus(status) {
|
function notifyStatus(status) {
|
||||||
ipcMain.emit('local-server-status-events', status);
|
ipcMain.emit('local-controller-status-events', status);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterOutput(line) {
|
function filterOutput(line) {
|
||||||
@ -120,44 +120,44 @@ function filterOutput(line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function stopAll() {
|
async function stopAll() {
|
||||||
for(var serverName in runningServers) {
|
for(var controllerName in runningControllers) {
|
||||||
let result, error = await stop(serverName);
|
let result, error = await stop(controllerName);
|
||||||
}
|
}
|
||||||
console.log(`Stopped all servers`);
|
console.log(`Stopped all controllers`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stop(serverName) {
|
async function stop(controllerName) {
|
||||||
let pid = undefined;
|
let pid = undefined;
|
||||||
|
|
||||||
const runningServer = runningServers[serverName];
|
const runningController = runningControllers[controllerName];
|
||||||
|
|
||||||
if(runningServer !== undefined && runningServer.process) {
|
if(runningController !== undefined && runningController.process) {
|
||||||
pid = runningServer.process.pid;
|
pid = runningController.process.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Stopping '${serverName}' with PID='${pid}'`);
|
console.log(`Stopping '${controllerName}' with PID='${pid}'`);
|
||||||
|
|
||||||
const stopped = new Promise((resolve, reject) => {
|
const stopped = new Promise((resolve, reject) => {
|
||||||
if(pid === undefined) {
|
if(pid === undefined) {
|
||||||
resolve(`Server '${serverName} is already stopped`);
|
resolve(`Controller '${controllerName} is already stopped`);
|
||||||
delete runningServers[serverName];
|
delete runningControllers[controllerName];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
kill(pid, (error) => {
|
kill(pid, (error) => {
|
||||||
if(error) {
|
if(error) {
|
||||||
console.error(`Error occured during stopping '${serverName}' with PID='${pid}'`);
|
console.error(`Error occured during stopping '${controllerName}' with PID='${pid}'`);
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete runningServers[serverName];
|
delete runningControllers[controllerName];
|
||||||
console.log(`Stopped '${serverName}' with PID='${pid}'`);
|
console.log(`Stopped '${controllerName}' with PID='${pid}'`);
|
||||||
resolve(`Stopped '${serverName}' with PID='${pid}'`);
|
resolve(`Stopped '${controllerName}' with PID='${pid}'`);
|
||||||
|
|
||||||
notifyStatus({
|
notifyStatus({
|
||||||
serverName: serverName,
|
controllerName: controllerName,
|
||||||
status: 'stopped',
|
status: 'stopped',
|
||||||
message: `Server '${serverName}' stopped'`
|
message: `Controller '${controllerName}' stopped'`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -166,11 +166,11 @@ async function stop(serverName) {
|
|||||||
return stopped;
|
return stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getIniFile(server) {
|
async function getIniFile(controller) {
|
||||||
return path.join(app.getPath('userData'), `gns3_server_${server.id}.ini`);
|
return path.join(app.getPath('userData'), `gns3_controller_${controller.id}.ini`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function configure(configPath, server) {
|
async function configure(configPath, controller) {
|
||||||
if(!fs.existsSync(configPath)) {
|
if(!fs.existsSync(configPath)) {
|
||||||
fs.closeSync(fs.openSync(configPath, 'w'));
|
fs.closeSync(fs.openSync(configPath, 'w'));
|
||||||
console.log(`Configuration file '${configPath}' has been created.`);
|
console.log(`Configuration file '${configPath}' has been created.`);
|
||||||
@ -178,20 +178,20 @@ async function configure(configPath, server) {
|
|||||||
|
|
||||||
var config = ini.parse(fs.readFileSync(configPath, 'utf-8'));
|
var config = ini.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
|
||||||
if(server.path) {
|
if(controller.path) {
|
||||||
config.path = server.path;
|
config.path = controller.path;
|
||||||
}
|
}
|
||||||
if(server.host) {
|
if(controller.host) {
|
||||||
config.host = server.host;
|
config.host = controller.host;
|
||||||
}
|
}
|
||||||
if(server.port) {
|
if(controller.port) {
|
||||||
config.port = server.port;
|
config.port = controller.port;
|
||||||
}
|
}
|
||||||
if(server.ubridge_path) {
|
if(controller.ubridge_path) {
|
||||||
config.ubridge_path = server.ubridge_path;
|
config.ubridge_path = controller.ubridge_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(configPath, ini.stringify(config, { section: 'Server' }));
|
fs.writeFileSync(configPath, ini.stringify(config, { section: 'Controller' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setPATHEnv() {
|
async function setPATHEnv() {
|
||||||
@ -216,7 +216,7 @@ async function setPATHEnv() {
|
|||||||
process.env.PATH = extra.join(";");
|
process.env.PATH = extra.join(";");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run(server, options) {
|
async function run(controller, options) {
|
||||||
if(!options) {
|
if(!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
@ -226,34 +226,34 @@ async function run(server, options) {
|
|||||||
|
|
||||||
console.log(`Configuring`);
|
console.log(`Configuring`);
|
||||||
|
|
||||||
const configPath = await getIniFile(server);
|
const configPath = await getIniFile(controller);
|
||||||
await configure(configPath, server);
|
await configure(configPath, controller);
|
||||||
|
|
||||||
console.log(`Setting up PATH`);
|
console.log(`Setting up PATH`);
|
||||||
await setPATHEnv();
|
await setPATHEnv();
|
||||||
|
|
||||||
console.log(`Running '${server.path}'`);
|
console.log(`Running '${controller.path}'`);
|
||||||
|
|
||||||
let serverProcess = spawn(server.path, getServerArguments(server, {}, configPath));
|
let controllerProcess = spawn(controller.path, getControllerArguments(controller, {}, configPath));
|
||||||
|
|
||||||
notifyStatus({
|
notifyStatus({
|
||||||
serverName: server.name,
|
controllerName: controller.name,
|
||||||
status: 'started',
|
status: 'started',
|
||||||
message: `Server '${server.name}' started'`
|
message: `Controller '${controller.name}' started'`
|
||||||
});
|
});
|
||||||
|
|
||||||
runningServers[server.name] = {
|
runningControllers[controller.name] = {
|
||||||
process: serverProcess
|
process: controllerProcess
|
||||||
};
|
};
|
||||||
|
|
||||||
serverProcess.stdout.on('data', function(data) {
|
controllerProcess.stdout.on('data', function(data) {
|
||||||
const line = data.toString();
|
const line = data.toString();
|
||||||
const { isCritical, errorMessage } = filterOutput(line);
|
const { isCritical, errorMessage } = filterOutput(line);
|
||||||
if(isCritical) {
|
if(isCritical) {
|
||||||
notifyStatus({
|
notifyStatus({
|
||||||
serverName: server.name,
|
controllerName: controller.name,
|
||||||
status: 'stderr',
|
status: 'stderr',
|
||||||
message: `Server reported error: '${errorMessage}`
|
message: `Controller reported error: '${errorMessage}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,25 +262,25 @@ async function run(server, options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.stderr.on('data', function(data) {
|
controllerProcess.stderr.on('data', function(data) {
|
||||||
if(logSterr) {
|
if(logSterr) {
|
||||||
console.log(data.toString());
|
console.log(data.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('exit', (code, signal) => {
|
controllerProcess.on('exit', (code, signal) => {
|
||||||
notifyStatus({
|
notifyStatus({
|
||||||
serverName: server.name,
|
controllerName: controller.name,
|
||||||
status: 'errored',
|
status: 'errored',
|
||||||
message: `Server '${server.name}' has exited with status='${code}'`
|
message: `controller '${controller.name}' has exited with status='${code}'`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('error', (err) => {
|
controllerProcess.on('error', (err) => {
|
||||||
notifyStatus({
|
notifyStatus({
|
||||||
serverName: server.name,
|
controllerName: controller.name,
|
||||||
status: 'errored',
|
status: 'errored',
|
||||||
message: `Server errored: '${err}`
|
message: `Controller errored: '${err}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -297,9 +297,9 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ipcMain) {
|
if(ipcMain) {
|
||||||
ipcMain.on('local-server-run', async function (event, server) {
|
ipcMain.on('local-controller-run', async function (event, controller) {
|
||||||
const responseChannel = getChannelForServer();
|
const responseChannel = getChannelForController();
|
||||||
await run(server);
|
await run(controller);
|
||||||
event.sender.send(responseChannel, {
|
event.sender.send(responseChannel, {
|
||||||
success: true
|
success: true
|
||||||
});
|
});
|
||||||
@ -318,4 +318,4 @@ if (require.main === module) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
main();
|
main();
|
||||||
}
|
}
|
@ -36,8 +36,8 @@
|
|||||||
"generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv",
|
"generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv",
|
||||||
"prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts",
|
"prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts",
|
||||||
"postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts",
|
"postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts",
|
||||||
"postinstall": "ngcc --properties es5 browser module main --first-only --create-ivy-entry-points && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
"postinstall": "ngcc --properties es5 browser module main --first-only --create-ivy-entry-points --tsconfig \"./src/tsconfig.app.json\" && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig \"./src/tsconfig.app.json\"",
|
||||||
"snyk-protect": "snyk protect",
|
"snyk-protect": "snyk-protect",
|
||||||
"prepare": "yarn run snyk-protect"
|
"prepare": "yarn run snyk-protect"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -53,6 +53,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "^13.3.5",
|
"@angular/platform-browser-dynamic": "^13.3.5",
|
||||||
"@angular/router": "^13.3.5",
|
"@angular/router": "^13.3.5",
|
||||||
"@sentry/browser": "^6.14.1",
|
"@sentry/browser": "^6.14.1",
|
||||||
|
"@snyk/protect": "^1.972.0",
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/mocha": "^9.1.1",
|
"@types/mocha": "^9.1.1",
|
||||||
"@types/react": "^17.0.34",
|
"@types/react": "^17.0.34",
|
||||||
@ -82,7 +83,6 @@
|
|||||||
"rxjs": "^6.6.7",
|
"rxjs": "^6.6.7",
|
||||||
"rxjs-compat": "^6.6.7",
|
"rxjs-compat": "^6.6.7",
|
||||||
"save-svg-as-png": "^1.4.17",
|
"save-svg-as-png": "^1.4.17",
|
||||||
"snyk": "^1.915.0",
|
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"svg-crowbar": "^0.7.0",
|
"svg-crowbar": "^0.7.0",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
|
@ -67,7 +67,7 @@ GNS3 Web UI 2020.2.0-beta.4
|
|||||||
Bug Fixes
|
Bug Fixes
|
||||||
- New port setting for GNS3 VM preferences
|
- New port setting for GNS3 VM preferences
|
||||||
- Option to auto-hide menu toolbar on the left side
|
- Option to auto-hide menu toolbar on the left side
|
||||||
- Server type in template preferences
|
-Controller type in template preferences
|
||||||
- Error when selecting existing Docker image
|
- Error when selecting existing Docker image
|
||||||
- Default values in templates
|
- Default values in templates
|
||||||
- TypeError: Cannot read property 'message' of undefined
|
- TypeError: Cannot read property 'message' of undefined
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component';
|
import { BundledControllerFinderComponent } from './components/bundled-controller-finder/bundled-controller-finder.component';
|
||||||
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
|
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
|
||||||
import { HelpComponent } from './components/help/help.component';
|
import { HelpComponent } from './components/help/help.component';
|
||||||
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
|
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
|
||||||
@ -44,7 +44,7 @@ import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs
|
|||||||
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
|
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
|
||||||
import { ProjectMapComponent } from './components/project-map/project-map.component';
|
import { ProjectMapComponent } from './components/project-map/project-map.component';
|
||||||
import { ProjectsComponent } from './components/projects/projects.component';
|
import { ProjectsComponent } from './components/projects/projects.component';
|
||||||
import { ServersComponent } from './components/servers/servers.component';
|
import { ControllersComponent } from './components/controllers/controllers.component';
|
||||||
import { ConsoleComponent } from './components/settings/console/console.component';
|
import { ConsoleComponent } from './components/settings/console/console.component';
|
||||||
import { SettingsComponent } from './components/settings/settings.component';
|
import { SettingsComponent } from './components/settings/settings.component';
|
||||||
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
|
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
|
||||||
@ -53,172 +53,257 @@ import { WebConsoleFullWindowComponent } from './components/web-console-full-win
|
|||||||
import { ConsoleGuard } from './guards/console-guard';
|
import { ConsoleGuard } from './guards/console-guard';
|
||||||
import { LoginGuard } from './guards/login-guard';
|
import { LoginGuard } from './guards/login-guard';
|
||||||
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
|
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
|
||||||
import { ServerResolve } from './resolvers/server-resolve';
|
import { ControllerResolve } from './resolvers/controller-resolve';
|
||||||
import { UserManagementComponent } from './components/user-management/user-management.component';
|
import { UserManagementComponent } from './components/user-management/user-management.component';
|
||||||
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
||||||
import { ImageManagerComponent } from './components/image-manager/image-manager.component';
|
import { ImageManagerComponent } from './components/image-manager/image-manager.component';
|
||||||
|
import { UserDetailComponent } from "./components/user-management/user-detail/user-detail.component";
|
||||||
|
import { UserDetailResolver } from "./resolvers/user-detail.resolver";
|
||||||
|
import { ManagementComponent } from "./components/management/management.component";
|
||||||
|
import { PermissionResolver } from "./resolvers/permission.resolver";
|
||||||
|
import { UserGroupsResolver } from "./resolvers/user-groups.resolver";
|
||||||
|
import { UserPermissionsResolver } from "./resolvers/user-permissions.resolver";
|
||||||
|
import { GroupManagementComponent } from "./components/group-management/group-management.component";
|
||||||
|
import { RoleManagementComponent } from "./components/role-management/role-management.component";
|
||||||
|
import { PermissionsManagementComponent } from "./components/permissions-management/permissions-management.component";
|
||||||
|
import { GroupDetailsComponent } from "./components/group-details/group-details.component";
|
||||||
|
import { GroupMembersResolver } from "./resolvers/group-members.resolver";
|
||||||
|
import { GroupResolver } from "./resolvers/group.resolver";
|
||||||
|
import { GroupRoleResolver } from "./resolvers/group-role.resolver";
|
||||||
|
import { RoleDetailComponent } from "./components/role-management/role-detail/role-detail.component";
|
||||||
|
import { RoleDetailResolver } from "./resolvers/role-detail.resolver";
|
||||||
|
import { RolePermissionsComponent } from "./components/role-management/role-detail/role-permissions/role-permissions.component";
|
||||||
|
import { UserPermissionsComponent } from "./components/user-management/user-detail/user-permissions/user-permissions.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: DefaultLayoutComponent,
|
component: DefaultLayoutComponent,
|
||||||
children: [
|
children: [
|
||||||
{ path: '', redirectTo: 'servers', pathMatch: 'full' },
|
{ path: '', redirectTo: 'controllers', pathMatch: 'full' },
|
||||||
{ path: 'servers', component: ServersComponent },
|
{ path: 'controllers', component: ControllersComponent },
|
||||||
{ path: 'bundled', component: BundledServerFinderComponent },
|
{ path: 'bundled', component: BundledControllerFinderComponent },
|
||||||
{ path: 'server/:server_id/login', component: LoginComponent },
|
{ path: 'controller/:controller_id/login', component: LoginComponent },
|
||||||
{ path: 'server/:server_id/loggeduser', component: LoggedUserComponent },
|
{ path: 'controller/:controller_id/loggeduser', component: LoggedUserComponent },
|
||||||
{path : 'server/:server_id/image-manager', component: ImageManagerComponent},
|
{path : 'controller/:controller_id/image-manager', component: ImageManagerComponent},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/projects',
|
path: 'controller/:controller_id/projects',
|
||||||
component: ProjectsComponent,
|
component: ProjectsComponent,
|
||||||
canActivate: [LoginGuard],
|
canActivate: [LoginGuard],
|
||||||
resolve: { server: ServerResolve },
|
resolve: { controller: ControllerResolve },
|
||||||
},
|
},
|
||||||
{ path: 'help', component: HelpComponent },
|
{ path: 'help', component: HelpComponent },
|
||||||
{ path: 'settings', component: SettingsComponent },
|
{ path: 'settings', component: SettingsComponent },
|
||||||
{ path: 'settings/console', component: ConsoleComponent },
|
{ path: 'settings/console', component: ConsoleComponent },
|
||||||
{ path: 'installed-software', component: InstalledSoftwareComponent },
|
|
||||||
{ path: 'server/:server_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard] },
|
|
||||||
|
|
||||||
{ path: 'server/:server_ip/:server_port/project/:project_id', component: DirectLinkComponent, canActivate: [LoginGuard] },
|
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/project/:project_id/snapshots',
|
path: 'controller/:controller_id/management/users/:user_id',
|
||||||
|
component: UserDetailComponent,
|
||||||
|
canActivate: [LoginGuard],
|
||||||
|
resolve: {
|
||||||
|
user: UserDetailResolver,
|
||||||
|
groups: UserGroupsResolver,
|
||||||
|
permissions: UserPermissionsResolver,
|
||||||
|
controller: ControllerResolve},
|
||||||
|
},
|
||||||
|
{ path: 'installed-software', component: InstalledSoftwareComponent },
|
||||||
|
{ path: 'controller/:controller_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
|
{ path: 'controller/:controller_ip/:controller_port/project/:project_id', component: DirectLinkComponent, canActivate: [LoginGuard] },
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/project/:project_id/snapshots',
|
||||||
component: ListOfSnapshotsComponent,
|
component: ListOfSnapshotsComponent,
|
||||||
canActivate: [LoginGuard],
|
canActivate: [LoginGuard],
|
||||||
resolve: { server: ServerResolve },
|
resolve: { controller: ControllerResolve },
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/preferences', component: PreferencesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences', component: PreferencesComponent, canActivate: [LoginGuard] },
|
||||||
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
|
// { path: 'controller/:controller_id/preferences/general', component: GeneralPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/builtin', component: BuiltInPreferencesComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
{ path: 'server/:server_id/preferences/builtin/ethernet-hubs', component: EthernetHubsTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/builtin/ethernet-hubs', component: EthernetHubsTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/ethernet-hubs/addtemplate',
|
path: 'controller/:controller_id/preferences/builtin/ethernet-hubs/addtemplate',
|
||||||
component: EthernetHubsAddTemplateComponent,
|
component: EthernetHubsAddTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/ethernet-hubs/:template_id',
|
path: 'controller/:controller_id/preferences/builtin/ethernet-hubs/:template_id',
|
||||||
component: EthernetHubsTemplateDetailsComponent,
|
component: EthernetHubsTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/ethernet-switches',
|
path: 'controller/:controller_id/preferences/builtin/ethernet-switches',
|
||||||
component: EthernetSwitchesTemplatesComponent,
|
component: EthernetSwitchesTemplatesComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/ethernet-switches/addtemplate',
|
path: 'controller/:controller_id/preferences/builtin/ethernet-switches/addtemplate',
|
||||||
component: EthernetSwitchesAddTemplateComponent,
|
component: EthernetSwitchesAddTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/ethernet-switches/:template_id',
|
path: 'controller/:controller_id/preferences/builtin/ethernet-switches/:template_id',
|
||||||
component: EthernetSwitchesTemplateDetailsComponent,
|
component: EthernetSwitchesTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
|
|
||||||
{ path: 'server/:server_id/preferences/builtin/cloud-nodes', component: CloudNodesTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/builtin/cloud-nodes', component: CloudNodesTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/cloud-nodes/addtemplate',
|
path: 'controller/:controller_id/preferences/builtin/cloud-nodes/addtemplate',
|
||||||
component: CloudNodesAddTemplateComponent,
|
component: CloudNodesAddTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/builtin/cloud-nodes/:template_id',
|
path: 'controller/:controller_id/preferences/builtin/cloud-nodes/:template_id',
|
||||||
component: CloudNodesTemplateDetailsComponent,
|
component: CloudNodesTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
|
|
||||||
//{ path: 'server/:server_id/preferences/dynamips', component: DynamipsPreferencesComponent },
|
//{ path: 'controller/:controller_id/preferences/dynamips', component: DynamipsPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/dynamips/templates', component: IosTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/dynamips/templates', component: IosTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/dynamips/templates/addtemplate', component: AddIosTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/dynamips/templates/addtemplate', component: AddIosTemplateComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/dynamips/templates/:template_id', component: IosTemplateDetailsComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/dynamips/templates/:template_id', component: IosTemplateDetailsComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/dynamips/templates/:template_id/copy',
|
path: 'controller/:controller_id/preferences/dynamips/templates/:template_id/copy',
|
||||||
component: CopyIosTemplateComponent,
|
component: CopyIosTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
|
|
||||||
// { path: 'server/:server_id/preferences/qemu', component: QemuPreferencesComponent },
|
// { path: 'controller/:controller_id/preferences/qemu', component: QemuPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/qemu/templates', component: QemuVmTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/qemu/templates', component: QemuVmTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/qemu/templates/:template_id/copy',
|
path: 'controller/:controller_id/preferences/qemu/templates/:template_id/copy',
|
||||||
component: CopyQemuVmTemplateComponent,
|
component: CopyQemuVmTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/preferences/qemu/templates/:template_id', component: QemuVmTemplateDetailsComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/qemu/templates/:template_id', component: QemuVmTemplateDetailsComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/qemu/addtemplate', component: AddQemuVmTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/qemu/addtemplate', component: AddQemuVmTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
// { path: 'server/:server_id/preferences/vpcs', component: VpcsPreferencesComponent },
|
// { path: 'controller/:controller_id/preferences/vpcs', component: VpcsPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/vpcs/templates', component: VpcsTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/vpcs/templates', component: VpcsTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/vpcs/templates/:template_id', component: VpcsTemplateDetailsComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/vpcs/templates/:template_id', component: VpcsTemplateDetailsComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
// { path: 'server/:server_id/preferences/virtualbox', component: VirtualBoxPreferencesComponent },
|
// { path: 'controller/:controller_id/preferences/virtualbox', component: VirtualBoxPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/virtualbox/templates/:template_id',
|
path: 'controller/:controller_id/preferences/virtualbox/templates/:template_id',
|
||||||
component: VirtualBoxTemplateDetailsComponent,
|
component: VirtualBoxTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
// { path: 'server/:server_id/preferences/vmware', component: VmwarePreferencesComponent },
|
// { path: 'controller/:controller_id/preferences/vmware', component: VmwarePreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/vmware/templates', component: VmwareTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/vmware/templates', component: VmwareTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/vmware/templates/:template_id',
|
path: 'controller/:controller_id/preferences/vmware/templates/:template_id',
|
||||||
component: VmwareTemplateDetailsComponent,
|
component: VmwareTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
{ path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/docker/templates', component: DockerTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/docker/templates/:template_id',
|
path: 'controller/:controller_id/preferences/docker/templates/:template_id',
|
||||||
component: DockerTemplateDetailsComponent,
|
component: DockerTemplateDetailsComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/preferences/docker/templates/:template_id/copy',
|
path: 'controller/:controller_id/preferences/docker/templates/:template_id/copy',
|
||||||
component: CopyDockerTemplateComponent,
|
component: CopyDockerTemplateComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/preferences/docker/addtemplate', component: AddDockerTemplateComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id/preferences/docker/addtemplate', component: AddDockerTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
{ path: 'controller/:controller_id/preferences/iou/templates', component: IouTemplatesComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/iou/templates', component: IouTemplatesComponent, canActivate: [LoginGuard] },
|
{ path: 'controller/:controller_id//preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent, canActivate: [LoginGuard] },
|
||||||
{ path: 'server/:server_id/preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent, canActivate: [LoginGuard] },
|
{
|
||||||
{ path: 'server/:server_id/preferences/iou/templates/:template_id/copy', component: CopyIouTemplateComponent, canActivate: [LoginGuard] },
|
path: 'controller/:controller_id/preferences/iou/templates/:template_id/copy',
|
||||||
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent, canActivate: [LoginGuard] },
|
component: CopyIouTemplateComponent,
|
||||||
|
canActivate: [LoginGuard]
|
||||||
|
},
|
||||||
|
{ path: 'controller/:controller_id/preferences/iou/addtemplate', component: AddIouTemplateComponent, canActivate: [LoginGuard] },
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/management',
|
||||||
|
component: ManagementComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'users',
|
||||||
|
component: UserManagementComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'groups',
|
||||||
|
component: GroupManagementComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'roles',
|
||||||
|
component: RoleManagementComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'permissions',
|
||||||
|
component: PermissionsManagementComponent
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/management/groups/:user_group_id',
|
||||||
|
component: GroupDetailsComponent,
|
||||||
|
resolve: {
|
||||||
|
members: GroupMembersResolver,
|
||||||
|
controller: ControllerResolve,
|
||||||
|
group: GroupResolver,
|
||||||
|
roles: GroupRoleResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/management/roles/:role_id',
|
||||||
|
component: RoleDetailComponent,
|
||||||
|
resolve: {
|
||||||
|
role: RoleDetailResolver,
|
||||||
|
controller: ControllerResolve
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/management/roles/:role_id/permissions',
|
||||||
|
component: RolePermissionsComponent,
|
||||||
|
resolve: {
|
||||||
|
role: RoleDetailResolver,
|
||||||
|
controller: ControllerResolve,
|
||||||
|
permissions: PermissionResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'controller/:controller_id/management/users/:user_id/permissions',
|
||||||
|
component: UserPermissionsComponent,
|
||||||
|
resolve: {
|
||||||
|
user: UserDetailResolver,
|
||||||
|
userPermissions: UserPermissionsResolver,
|
||||||
|
controller: ControllerResolve,
|
||||||
|
permissions: PermissionResolver
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/project/:project_id',
|
path: 'controller/:controller_id/project/:project_id',
|
||||||
component: ProjectMapComponent,
|
component: ProjectMapComponent,
|
||||||
canActivate: [LoginGuard],
|
canActivate: [LoginGuard],
|
||||||
canDeactivate: [ConsoleGuard],
|
canDeactivate: [ConsoleGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'server/:server_id/project/:project_id/nodes/:node_id',
|
path: 'controller/:controller_id/project/:project_id/nodes/:node_id',
|
||||||
component: WebConsoleFullWindowComponent,
|
component: WebConsoleFullWindowComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'static/web-ui/server/:server_id/project/:project_id/nodes/:node_id',
|
path: 'static/web-ui/controller/:controller_id/project/:project_id/nodes/:node_id',
|
||||||
component: WebConsoleFullWindowComponent,
|
component: WebConsoleFullWindowComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'user_management',
|
|
||||||
component: UserManagementComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
component: PageNotFoundComponent,
|
component: PageNotFoundComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -231,4 +316,5 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {}
|
export class AppRoutingModule {
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* tslint:disable */
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { OverlayModule } from '@angular/cdk/overlay';
|
import { OverlayModule } from '@angular/cdk/overlay';
|
||||||
import { CdkTableModule } from '@angular/cdk/table';
|
import { CdkTableModule } from '@angular/cdk/table';
|
||||||
@ -29,7 +30,7 @@ import { ProgressDialogService } from './common/progress-dialog/progress-dialog.
|
|||||||
import { ProgressComponent } from './common/progress/progress.component';
|
import { ProgressComponent } from './common/progress/progress.component';
|
||||||
import { ProgressService } from './common/progress/progress.service';
|
import { ProgressService } from './common/progress/progress.service';
|
||||||
import { AdbutlerComponent } from './components/adbutler/adbutler.component';
|
import { AdbutlerComponent } from './components/adbutler/adbutler.component';
|
||||||
import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component';
|
import { BundledControllerFinderComponent } from './components/bundled-controller-finder/bundled-controller-finder.component';
|
||||||
import { InformationDialogComponent } from './components/dialogs/information-dialog.component';
|
import { InformationDialogComponent } from './components/dialogs/information-dialog.component';
|
||||||
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
|
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
|
||||||
import { DrawingAddedComponent } from './components/drawings-listeners/drawing-added/drawing-added.component';
|
import { DrawingAddedComponent } from './components/drawings-listeners/drawing-added/drawing-added.component';
|
||||||
@ -132,7 +133,7 @@ import { StartNodeActionComponent } from './components/project-map/context-menu/
|
|||||||
import { StopCaptureActionComponent } from './components/project-map/context-menu/actions/stop-capture/stop-capture-action.component';
|
import { StopCaptureActionComponent } from './components/project-map/context-menu/actions/stop-capture/stop-capture-action.component';
|
||||||
import { IsolateNodeActionComponent } from './components/project-map/context-menu/actions/isolate-node-action/isolate-node-action.component';
|
import { IsolateNodeActionComponent } from './components/project-map/context-menu/actions/isolate-node-action/isolate-node-action.component';
|
||||||
import { UnisolateNodeActionComponent } from './components/project-map/context-menu/actions/unisolate-node-action/unisolate-node-action.component';
|
import { UnisolateNodeActionComponent } from './components/project-map/context-menu/actions/unisolate-node-action/unisolate-node-action.component';
|
||||||
import { StopNodeActionComponent } from './components/project-map/context-menu/actions/stop-node-action/stop-node-action.component';
|
import {StopNodeActionComponent } from './components/project-map/context-menu/actions/stop-node-action/stop-node-action.component';
|
||||||
import { SuspendLinkActionComponent } from './components/project-map/context-menu/actions/suspend-link/suspend-link-action.component';
|
import { SuspendLinkActionComponent } from './components/project-map/context-menu/actions/suspend-link/suspend-link-action.component';
|
||||||
import { SuspendNodeActionComponent } from './components/project-map/context-menu/actions/suspend-node-action/suspend-node-action.component';
|
import { SuspendNodeActionComponent } from './components/project-map/context-menu/actions/suspend-node-action/suspend-node-action.component';
|
||||||
import { ContextMenuComponent } from './components/project-map/context-menu/context-menu.component';
|
import { ContextMenuComponent } from './components/project-map/context-menu/context-menu.component';
|
||||||
@ -187,9 +188,9 @@ import { ProjectNameValidator } from './components/projects/models/projectNameVa
|
|||||||
import { NavigationDialogComponent } from './components/projects/navigation-dialog/navigation-dialog.component';
|
import { NavigationDialogComponent } from './components/projects/navigation-dialog/navigation-dialog.component';
|
||||||
import { ProjectsComponent } from './components/projects/projects.component';
|
import { ProjectsComponent } from './components/projects/projects.component';
|
||||||
import { SaveProjectDialogComponent } from './components/projects/save-project-dialog/save-project-dialog.component';
|
import { SaveProjectDialogComponent } from './components/projects/save-project-dialog/save-project-dialog.component';
|
||||||
import { AddServerDialogComponent } from './components/servers/add-server-dialog/add-server-dialog.component';
|
import { AddControllerDialogComponent } from './components/controllers/add-controller-dialog/add-controller-dialog.component';
|
||||||
import { ServerDiscoveryComponent } from './components/servers/server-discovery/server-discovery.component';
|
import { ControllerDiscoveryComponent } from './components/controllers/controller-discovery/controller-discovery.component';
|
||||||
import { ServersComponent } from './components/servers/servers.component';
|
import { ControllersComponent } from './components/controllers/controllers.component';
|
||||||
import { ConsoleComponent } from './components/settings/console/console.component';
|
import { ConsoleComponent } from './components/settings/console/console.component';
|
||||||
import { SettingsComponent } from './components/settings/settings.component';
|
import { SettingsComponent } from './components/settings/settings.component';
|
||||||
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
|
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
|
||||||
@ -214,7 +215,7 @@ import { LoginGuard } from './guards/login-guard';
|
|||||||
import { ProjectWebServiceHandler } from './handlers/project-web-service-handler';
|
import { ProjectWebServiceHandler } from './handlers/project-web-service-handler';
|
||||||
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
|
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
|
||||||
import { MATERIAL_IMPORTS } from './material.imports';
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
import { ServerResolve } from './resolvers/server-resolve';
|
import { ControllerResolve } from './resolvers/controller-resolve';
|
||||||
import { ApplianceService } from './services/appliances.service';
|
import { ApplianceService } from './services/appliances.service';
|
||||||
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
|
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
|
||||||
import { BuiltInTemplatesService } from './services/built-in-templates.service';
|
import { BuiltInTemplatesService } from './services/built-in-templates.service';
|
||||||
@ -224,7 +225,7 @@ import { DockerService } from './services/docker.service';
|
|||||||
import { DrawingService } from './services/drawing.service';
|
import { DrawingService } from './services/drawing.service';
|
||||||
import { ExternalSoftwareDefinitionService } from './services/external-software-definition.service';
|
import { ExternalSoftwareDefinitionService } from './services/external-software-definition.service';
|
||||||
import { GoogleAnalyticsService } from './services/google-analytics.service';
|
import { GoogleAnalyticsService } from './services/google-analytics.service';
|
||||||
import { HttpServer, ServerErrorHandler } from './services/http-server.service';
|
import { HttpController, ControllerErrorHandler } from './services/http-controller.service';
|
||||||
import { InfoService } from './services/info.service';
|
import { InfoService } from './services/info.service';
|
||||||
import { InstalledSoftwareService } from './services/installed-software.service';
|
import { InstalledSoftwareService } from './services/installed-software.service';
|
||||||
import { IosConfigurationService } from './services/ios-configuration.service';
|
import { IosConfigurationService } from './services/ios-configuration.service';
|
||||||
@ -243,10 +244,10 @@ import { ProjectService } from './services/project.service';
|
|||||||
import { QemuConfigurationService } from './services/qemu-configuration.service';
|
import { QemuConfigurationService } from './services/qemu-configuration.service';
|
||||||
import { QemuService } from './services/qemu.service';
|
import { QemuService } from './services/qemu.service';
|
||||||
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
|
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
|
||||||
import { ServerManagementService } from './services/server-management.service';
|
import { ControllerManagementService } from './services/controller-management.service';
|
||||||
import { ServerSettingsService } from './services/server-settings.service';
|
import { ControllerSettingsService } from './services/controller-settings.service';
|
||||||
import { ServerDatabase } from './services/server.database';
|
import { ControllerDatabase } from './services/controller.database';
|
||||||
import { ServerService } from './services/server.service';
|
import { ControllerService } from './services/controller.service';
|
||||||
import { SettingsService } from './services/settings.service';
|
import { SettingsService } from './services/settings.service';
|
||||||
import { ConsoleService } from './services/settings/console.service';
|
import { ConsoleService } from './services/settings/console.service';
|
||||||
import { DefaultConsoleService } from './services/settings/default-console.service';
|
import { DefaultConsoleService } from './services/settings/default-console.service';
|
||||||
@ -271,9 +272,50 @@ import { MarkedDirective } from './directives/marked.directive';
|
|||||||
import { LoginComponent } from './components/login/login.component';
|
import { LoginComponent } from './components/login/login.component';
|
||||||
import { LoginService } from './services/login.service';
|
import { LoginService } from './services/login.service';
|
||||||
import { HttpRequestsInterceptor } from './interceptors/http.interceptor';
|
import { HttpRequestsInterceptor } from './interceptors/http.interceptor';
|
||||||
import { UserManagementComponent } from './components/user-management/user-management.component'
|
import { UserManagementComponent } from './components/user-management/user-management.component';
|
||||||
import { UserService } from './services/user.service';
|
import { UserService } from './services/user.service';
|
||||||
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
||||||
|
import { AddUserDialogComponent } from './components/user-management/add-user-dialog/add-user-dialog.component';
|
||||||
|
import { UserFilterPipe } from './filters/user-filter.pipe';
|
||||||
|
import { GroupManagementComponent } from './components/group-management/group-management.component';
|
||||||
|
import { GroupFilterPipe } from './filters/group-filter.pipe';
|
||||||
|
import { AddGroupDialogComponent } from './components/group-management/add-group-dialog/add-group-dialog.component';
|
||||||
|
import { DeleteGroupDialogComponent } from './components/group-management/delete-group-dialog/delete-group-dialog.component';
|
||||||
|
import { DeleteUserDialogComponent } from './components/user-management/delete-user-dialog/delete-user-dialog.component';
|
||||||
|
import { GroupDetailsComponent } from './components/group-details/group-details.component';
|
||||||
|
import { UserDetailComponent } from './components/user-management/user-detail/user-detail.component';
|
||||||
|
import { AddUserToGroupDialogComponent } from './components/group-details/add-user-to-group-dialog/add-user-to-group-dialog.component';
|
||||||
|
import { RemoveToGroupDialogComponent } from '@components/group-details/remove-to-group-dialog/remove-to-group-dialog.component';
|
||||||
|
import { PaginatorPipe } from './components/group-details/paginator.pipe';
|
||||||
|
import { MembersFilterPipe } from './components/group-details/members-filter.pipe';
|
||||||
|
import { ManagementComponent } from './components/management/management.component';
|
||||||
|
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||||
|
import { RoleManagementComponent } from './components/role-management/role-management.component';
|
||||||
|
import { RoleFilterPipe } from './components/role-management/role-filter.pipe';
|
||||||
|
import { AddRoleDialogComponent } from './components/role-management/add-role-dialog/add-role-dialog.component';
|
||||||
|
import { DeleteRoleDialogComponent } from './components/role-management/delete-role-dialog/delete-role-dialog.component';
|
||||||
|
import { RoleDetailComponent } from './components/role-management/role-detail/role-detail.component';
|
||||||
|
import { PermissionEditorComponent } from './components/role-management/role-detail/permission-editor/permission-editor.component';
|
||||||
|
import { EditablePermissionComponent } from './components/role-management/role-detail/permission-editor/editable-permission/editable-permission.component';
|
||||||
|
import { PermissionEditorValidateDialogComponent } from './components/role-management/role-detail/permission-editor/permission-editor-validate-dialog/permission-editor-validate-dialog.component';
|
||||||
|
import { PermissionsManagementComponent } from './components/permissions-management/permissions-management.component';
|
||||||
|
import { PermissionEditLineComponent } from '@components/permissions-management/permission-edit-line/permission-edit-line.component';
|
||||||
|
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||||
|
import { UserPermissionsComponent } from './components/user-management/user-detail/user-permissions/user-permissions.component';
|
||||||
|
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||||
|
import {PathAutoCompleteComponent} from './components/permissions-management/add-permission-line/path-auto-complete/path-auto-complete.component';
|
||||||
|
import {FilterCompletePipe} from './components/permissions-management/add-permission-line/path-auto-complete/filter-complete.pipe';
|
||||||
|
import { AddPermissionLineComponent } from './components/permissions-management/add-permission-line/add-permission-line.component';
|
||||||
|
import { MethodButtonComponent } from './components/permissions-management/method-button/method-button.component';
|
||||||
|
import { ActionButtonComponent } from './components/permissions-management/action-button/action-button.component';
|
||||||
|
import { DeletePermissionDialogComponent } from './components/permissions-management/delete-permission-dialog/delete-permission-dialog.component';
|
||||||
|
import { AddRoleToGroupComponent } from './components/group-details/add-role-to-group/add-role-to-group.component';
|
||||||
|
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||||
|
import { PermissionsFilterPipe } from './components/permissions-management/permissions-filter.pipe';
|
||||||
|
import { DisplayPathPipe } from './components/permissions-management/display-path.pipe';
|
||||||
|
import {RolePermissionsComponent} from "@components/role-management/role-detail/role-permissions/role-permissions.component";
|
||||||
|
import { ChangeUserPasswordComponent } from './components/user-management/user-detail/change-user-password/change-user-password.component';
|
||||||
|
import {MatMenuModule} from "@angular/material/menu";
|
||||||
import { ImageManagerComponent } from './components/image-manager/image-manager.component';
|
import { ImageManagerComponent } from './components/image-manager/image-manager.component';
|
||||||
import { AddImageDialogComponent } from './components/image-manager/add-image-dialog/add-image-dialog.component';
|
import { AddImageDialogComponent } from './components/image-manager/add-image-dialog/add-image-dialog.component';
|
||||||
import { DeleteAllImageFilesDialogComponent } from './components/image-manager/deleteallfiles-dialog/deleteallfiles-dialog.component';
|
import { DeleteAllImageFilesDialogComponent } from './components/image-manager/deleteallfiles-dialog/deleteallfiles-dialog.component';
|
||||||
@ -288,8 +330,8 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
LoggedUserComponent,
|
LoggedUserComponent,
|
||||||
ProjectMapComponent,
|
ProjectMapComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
ServersComponent,
|
ControllersComponent,
|
||||||
AddServerDialogComponent,
|
AddControllerDialogComponent,
|
||||||
CreateSnapshotDialogComponent,
|
CreateSnapshotDialogComponent,
|
||||||
SnapshotMenuItemComponent,
|
SnapshotMenuItemComponent,
|
||||||
ProjectsComponent,
|
ProjectsComponent,
|
||||||
@ -320,9 +362,9 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
SuspendLinkActionComponent,
|
SuspendLinkActionComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
PreferencesComponent,
|
PreferencesComponent,
|
||||||
BundledServerFinderComponent,
|
BundledControllerFinderComponent,
|
||||||
ProgressComponent,
|
ProgressComponent,
|
||||||
ServerDiscoveryComponent,
|
ControllerDiscoveryComponent,
|
||||||
NodeSelectInterfaceComponent,
|
NodeSelectInterfaceComponent,
|
||||||
DrawLinkToolComponent,
|
DrawLinkToolComponent,
|
||||||
InstalledSoftwareComponent,
|
InstalledSoftwareComponent,
|
||||||
@ -471,6 +513,47 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
EditNetworkConfigurationDialogComponent,
|
EditNetworkConfigurationDialogComponent,
|
||||||
UserManagementComponent,
|
UserManagementComponent,
|
||||||
ProjectReadmeComponent,
|
ProjectReadmeComponent,
|
||||||
|
AddGroupDialogComponent,
|
||||||
|
GroupFilterPipe,
|
||||||
|
GroupManagementComponent,
|
||||||
|
AddUserDialogComponent,
|
||||||
|
UserFilterPipe,
|
||||||
|
DeleteGroupDialogComponent,
|
||||||
|
DeleteUserDialogComponent,
|
||||||
|
GroupDetailsComponent,
|
||||||
|
UserDetailComponent,
|
||||||
|
AddUserToGroupDialogComponent,
|
||||||
|
RemoveToGroupDialogComponent,
|
||||||
|
PaginatorPipe,
|
||||||
|
MembersFilterPipe,
|
||||||
|
ManagementComponent,
|
||||||
|
RoleManagementComponent,
|
||||||
|
RoleFilterPipe,
|
||||||
|
AddRoleDialogComponent,
|
||||||
|
DeleteRoleDialogComponent,
|
||||||
|
RoleDetailComponent,
|
||||||
|
PermissionEditorComponent,
|
||||||
|
EditablePermissionComponent,
|
||||||
|
PermissionEditorValidateDialogComponent,
|
||||||
|
RemoveToGroupDialogComponent,
|
||||||
|
PermissionsManagementComponent,
|
||||||
|
AddRoleToGroupComponent,
|
||||||
|
PermissionEditLineComponent,
|
||||||
|
AddPermissionLineComponent,
|
||||||
|
MethodButtonComponent,
|
||||||
|
ActionButtonComponent,
|
||||||
|
DeletePermissionDialogComponent,
|
||||||
|
PathAutoCompleteComponent,
|
||||||
|
FilterCompletePipe,
|
||||||
|
UserPermissionsComponent,
|
||||||
|
PermissionsFilterPipe,
|
||||||
|
RolePermissionsComponent,
|
||||||
|
DisplayPathPipe,
|
||||||
|
ChangeUserPasswordComponent,
|
||||||
|
FilterCompletePipe,
|
||||||
|
DisplayPathPipe,
|
||||||
|
ChangeUserPasswordComponent,
|
||||||
|
ProjectReadmeComponent,
|
||||||
ImageManagerComponent,
|
ImageManagerComponent,
|
||||||
AddImageDialogComponent,
|
AddImageDialogComponent,
|
||||||
DeleteAllImageFilesDialogComponent,
|
DeleteAllImageFilesDialogComponent,
|
||||||
@ -491,6 +574,8 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
NgxElectronModule,
|
NgxElectronModule,
|
||||||
FileUploadModule,
|
FileUploadModule,
|
||||||
MatSidenavModule,
|
MatSidenavModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatMenuModule,
|
||||||
ResizableModule,
|
ResizableModule,
|
||||||
DragAndDropModule,
|
DragAndDropModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
@ -498,21 +583,24 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
MATERIAL_IMPORTS,
|
MATERIAL_IMPORTS,
|
||||||
NgCircleProgressModule.forRoot(),
|
NgCircleProgressModule.forRoot(),
|
||||||
OverlayModule,
|
OverlayModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatAutocompleteModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SettingsService,
|
SettingsService,
|
||||||
{ provide: ErrorHandler, useClass: ToasterErrorHandler },
|
{provide: ErrorHandler, useClass: ToasterErrorHandler},
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpRequestsInterceptor, multi: true },
|
{provide: HTTP_INTERCEPTORS, useClass: HttpRequestsInterceptor, multi: true},
|
||||||
D3Service,
|
D3Service,
|
||||||
VersionService,
|
VersionService,
|
||||||
ProjectService,
|
ProjectService,
|
||||||
SymbolService,
|
SymbolService,
|
||||||
ServerService,
|
ControllerService,
|
||||||
TemplateService,
|
TemplateService,
|
||||||
NodeService,
|
NodeService,
|
||||||
LinkService,
|
LinkService,
|
||||||
DrawingService,
|
DrawingService,
|
||||||
HttpServer,
|
HttpController,
|
||||||
SnapshotService,
|
SnapshotService,
|
||||||
ProgressDialogService,
|
ProgressDialogService,
|
||||||
ToasterService,
|
ToasterService,
|
||||||
@ -525,11 +613,11 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
SelectionManager,
|
SelectionManager,
|
||||||
InRectangleHelper,
|
InRectangleHelper,
|
||||||
DrawingsDataSource,
|
DrawingsDataSource,
|
||||||
ServerErrorHandler,
|
ControllerErrorHandler,
|
||||||
ServerDatabase,
|
ControllerDatabase,
|
||||||
ProjectNameValidator,
|
ProjectNameValidator,
|
||||||
ToolsService,
|
ToolsService,
|
||||||
ServerSettingsService,
|
ControllerSettingsService,
|
||||||
QemuService,
|
QemuService,
|
||||||
VpcsService,
|
VpcsService,
|
||||||
TemplateMocksService,
|
TemplateMocksService,
|
||||||
@ -551,7 +639,7 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
IouService,
|
IouService,
|
||||||
IouConfigurationService,
|
IouConfigurationService,
|
||||||
RecentlyOpenedProjectService,
|
RecentlyOpenedProjectService,
|
||||||
ServerManagementService,
|
ControllerManagementService,
|
||||||
MapScaleService,
|
MapScaleService,
|
||||||
ConsoleService,
|
ConsoleService,
|
||||||
DefaultConsoleService,
|
DefaultConsoleService,
|
||||||
@ -566,7 +654,7 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
ThemeService,
|
ThemeService,
|
||||||
GoogleAnalyticsService,
|
GoogleAnalyticsService,
|
||||||
NodeConsoleService,
|
NodeConsoleService,
|
||||||
ServerResolve,
|
ControllerResolve,
|
||||||
LoginGuard,
|
LoginGuard,
|
||||||
ConsoleGuard,
|
ConsoleGuard,
|
||||||
Title,
|
Title,
|
||||||
@ -576,7 +664,7 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
|
|||||||
UserService
|
UserService
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AddServerDialogComponent,
|
AddControllerDialogComponent,
|
||||||
CreateSnapshotDialogComponent,
|
CreateSnapshotDialogComponent,
|
||||||
ProgressDialogComponent,
|
ProgressDialogComponent,
|
||||||
TemplateListDialogComponent,
|
TemplateListDialogComponent,
|
||||||
|
@ -44,6 +44,6 @@
|
|||||||
<app-drawing-resizing></app-drawing-resizing>
|
<app-drawing-resizing></app-drawing-resizing>
|
||||||
<app-selection-control></app-selection-control>
|
<app-selection-control></app-selection-control>
|
||||||
<app-selection-select></app-selection-select>
|
<app-selection-select></app-selection-select>
|
||||||
<app-text-editor #textEditor [server]="server" [svg]="svg"></app-text-editor>
|
<app-text-editor #textEditor [controller]="controller" [svg]="svg"></app-text-editor>
|
||||||
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
||||||
<app-link-editing [svg]="svg"></app-link-editing>
|
<app-link-editing [svg]="svg"></app-link-editing>
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -13,7 +13,7 @@ import { select, Selection } from 'd3-selection';
|
|||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { Link } from '../../../models/link';
|
import { Link } from '../../../models/link';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import { Controller } from '../../../models/controller';
|
||||||
import { Symbol } from '../../../models/symbol';
|
import { Symbol } from '../../../models/symbol';
|
||||||
import { MapScaleService } from '../../../services/mapScale.service';
|
import { MapScaleService } from '../../../services/mapScale.service';
|
||||||
import { MapSettingsService } from '../../../services/mapsettings.service';
|
import { MapSettingsService } from '../../../services/mapsettings.service';
|
||||||
@ -43,7 +43,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() drawings: Drawing[] = [];
|
@Input() drawings: Drawing[] = [];
|
||||||
@Input() symbols: Symbol[] = [];
|
@Input() symbols: Symbol[] = [];
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
@Input() server: Server;
|
@Input() controller: Controller;
|
||||||
|
|
||||||
@Input() width = 1500;
|
@Input() width = 1500;
|
||||||
@Input() height = 600;
|
@Input() height = 600;
|
||||||
|
@ -13,7 +13,7 @@ import { select } from 'd3-selection';
|
|||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { StyleProperty } from '../../../components/project-map/drawings-editors/text-editor/text-editor.component';
|
import { StyleProperty } from '../../../components/project-map/drawings-editors/text-editor/text-editor.component';
|
||||||
import { Link } from '../../../models/link';
|
import { Link } from '../../../models/link';
|
||||||
import { Server } from '../../../models/server';
|
import { Controller } from '../../../models/controller';
|
||||||
import { LinkService } from '../../../services/link.service';
|
import { LinkService } from '../../../services/link.service';
|
||||||
import { MapScaleService } from '../../../services/mapScale.service';
|
import { MapScaleService } from '../../../services/mapScale.service';
|
||||||
import { ToolsService } from '../../../services/tools.service';
|
import { ToolsService } from '../../../services/tools.service';
|
||||||
@ -37,7 +37,7 @@ import { Node } from '../../models/node';
|
|||||||
export class TextEditorComponent implements OnInit, OnDestroy {
|
export class TextEditorComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('temporaryTextElement') temporaryTextElement: ElementRef;
|
@ViewChild('temporaryTextElement') temporaryTextElement: ElementRef;
|
||||||
@Input('svg') svg: SVGSVGElement;
|
@Input('svg') svg: SVGSVGElement;
|
||||||
@Input('server') server: Server;
|
@Input('controller') controller: Controller;
|
||||||
|
|
||||||
leftPosition: string = '0px';
|
leftPosition: string = '0px';
|
||||||
topPosition: string = '0px';
|
topPosition: string = '0px';
|
||||||
@ -185,7 +185,7 @@ export class TextEditorComponent implements OnInit, OnDestroy {
|
|||||||
let link: Link = this.linksDataSource.get(this.editedLink.linkId);
|
let link: Link = this.linksDataSource.get(this.editedLink.linkId);
|
||||||
link.nodes.find((n) => n.node_id === this.editedNode.node_id).label.text = innerText;
|
link.nodes.find((n) => n.node_id === this.editedNode.node_id).label.text = innerText;
|
||||||
|
|
||||||
this.linkService.updateLink(this.server, link).subscribe((link: Link) => {
|
this.linkService.updateLink(this.controller, link).subscribe((link: Link) => {
|
||||||
rootElement
|
rootElement
|
||||||
.selectAll<SVGTextElement, TextElement>('text.editingMode')
|
.selectAll<SVGTextElement, TextElement>('text.editingMode')
|
||||||
.attr('visibility', 'visible')
|
.attr('visibility', 'visible')
|
||||||
|
@ -16,13 +16,13 @@ export class MapLink implements Indexed {
|
|||||||
suspend: boolean;
|
suspend: boolean;
|
||||||
link_style?: LinkStyle;
|
link_style?: LinkStyle;
|
||||||
|
|
||||||
distance: number; // this is not from server
|
distance: number; // this is not from controller
|
||||||
length: number; // this is not from server
|
length: number; // this is not from controller
|
||||||
source: MapNode; // this is not from server
|
source: MapNode; // this is not from controller
|
||||||
target: MapNode; // this is not from server
|
target: MapNode; // this is not from controller
|
||||||
|
|
||||||
isSelected = false; // this is not from server
|
isSelected = false; // this is not from controller
|
||||||
isMultiplied = false; // this is not from server
|
isMultiplied = false; // this is not from controller
|
||||||
x: number; // this is not from server
|
x: number; // this is not from controller
|
||||||
y: number; // this is not from server
|
y: number; // this is not from controller
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ export class UploadingProcessbarComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.upload_file_type = this.data.upload_file_type
|
this.upload_file_type = this.data.upload_file_type
|
||||||
debugger
|
|
||||||
this.subscription = this._US.currentCount.subscribe((count:number) => {
|
this.subscription = this._US.currentCount.subscribe((count:number) => {
|
||||||
this.uploadProgress = count;
|
this.uploadProgress = count;
|
||||||
if (this.uploadProgress === 100 || this.uploadProgress == null ) {
|
if (this.uploadProgress === 100 || this.uploadProgress == null ) {
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ProgressService } from '../../common/progress/progress.service';
|
||||||
|
import{ Controller } from '../../models/controller';
|
||||||
|
import { ControllerService } from '../../services/controller.service';
|
||||||
|
import { MockedControllerService } from '../../services/controller.service.spec';
|
||||||
|
import { MockedProgressService } from '../project-map/project-map.component.spec';
|
||||||
|
import { BundledControllerFinderComponent } from './bundled-controller-finder.component';
|
||||||
|
|
||||||
|
describe('BundledControllerFinderComponent', () => {
|
||||||
|
let component: BundledControllerFinderComponent;
|
||||||
|
let fixture: ComponentFixture<BundledControllerFinderComponent>;
|
||||||
|
let router: any;
|
||||||
|
let service: ControllerService;
|
||||||
|
let progressService: MockedProgressService = new MockedProgressService();
|
||||||
|
let controllerServiceMock: jasmine.SpyObj<ControllerService>;
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
controllerServiceMock = jasmine.createSpyObj<ControllerService>([
|
||||||
|
"getLocalController"
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: ControllerService, useValue: controllerServiceMock },
|
||||||
|
{ provide: ProgressService, useValue: progressService },
|
||||||
|
],
|
||||||
|
declarations: [BundledControllerFinderComponent],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(BundledControllerFinderComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and redirect to controller', fakeAsync(() => {
|
||||||
|
const controller = new Controller ();
|
||||||
|
controller.id = 99;
|
||||||
|
controllerServiceMock.getLocalController.and.returnValue(
|
||||||
|
Promise.resolve(controller)
|
||||||
|
);
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
tick(101)
|
||||||
|
fixture.detectChanges()
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(controllerServiceMock.getLocalController).toHaveBeenCalledWith('vps3.gns3.net',3000);
|
||||||
|
expect(router.navigate).toHaveBeenCalledWith(['/controller', 99, 'projects']);
|
||||||
|
})
|
||||||
|
service = TestBed.inject(ControllerService);
|
||||||
|
}));
|
||||||
|
});
|
@ -2,18 +2,18 @@ import { DOCUMENT } from '@angular/common';
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { ProgressService } from '../../common/progress/progress.service';
|
import { ProgressService } from '../../common/progress/progress.service';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
import { ServerService } from '../../services/server.service';
|
import { ControllerService } from '../../services/controller.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bundled-server-finder',
|
selector: 'app-bundled-controller-finder',
|
||||||
templateUrl: './bundled-server-finder.component.html',
|
templateUrl: './bundled-controller-finder.component.html',
|
||||||
styleUrls: ['./bundled-server-finder.component.scss'],
|
styleUrls: ['./bundled-controller-finder.component.scss'],
|
||||||
})
|
})
|
||||||
export class BundledServerFinderComponent implements OnInit {
|
export class BundledControllerFinderComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private serverService: ServerService,
|
private controllerService: ControllerService,
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
@Inject(DOCUMENT) private document
|
@Inject(DOCUMENT) private document
|
||||||
) {}
|
) {}
|
||||||
@ -31,8 +31,8 @@ export class BundledServerFinderComponent implements OnInit {
|
|||||||
port = 80;
|
port = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.serverService.getLocalServer(this.document.location.hostname, port).then((server: Server) => {
|
this.controllerService.getLocalController(this.document.location.hostname, port).then((controller:Controller ) => {
|
||||||
this.router.navigate(['/server', server.id, 'projects']);
|
this.router.navigate(['/controller', controller.id, 'projects']);
|
||||||
this.progressService.deactivate();
|
this.progressService.deactivate();
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
@ -1,67 +0,0 @@
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { ProgressService } from '../../common/progress/progress.service';
|
|
||||||
import { Server } from '../../models/server';
|
|
||||||
import { ServerService } from '../../services/server.service';
|
|
||||||
import { MockedServerService } from '../../services/server.service.spec';
|
|
||||||
import { MockedProgressService } from '../project-map/project-map.component.spec';
|
|
||||||
import { BundledServerFinderComponent } from './bundled-server-finder.component';
|
|
||||||
|
|
||||||
describe('BundledServerFinderComponent', () => {
|
|
||||||
let component: BundledServerFinderComponent;
|
|
||||||
let fixture: ComponentFixture<BundledServerFinderComponent>;
|
|
||||||
let router: any;
|
|
||||||
let service: ServerService;
|
|
||||||
let progressService: MockedProgressService = new MockedProgressService();
|
|
||||||
let serverServiceMock: jasmine.SpyObj<ServerService>;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
router = {
|
|
||||||
navigate: jasmine.createSpy('navigate'),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
serverServiceMock = jasmine.createSpyObj<ServerService>([
|
|
||||||
"getLocalServer"
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
// serverService = new MockedServerService();
|
|
||||||
// spyOn(serverService, 'getLocalServer').and.returnValue(Promise.resolve(server));
|
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: Router, useValue: router },
|
|
||||||
{ provide: ServerService, useValue: serverServiceMock },
|
|
||||||
{ provide: ProgressService, useValue: progressService },
|
|
||||||
],
|
|
||||||
declarations: [BundledServerFinderComponent],
|
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(BundledServerFinderComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create and redirect to server', fakeAsync(() => {
|
|
||||||
const server = new Server();
|
|
||||||
server.id = 99;
|
|
||||||
serverServiceMock.getLocalServer.and.returnValue(
|
|
||||||
Promise.resolve(server)
|
|
||||||
);
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
tick(101)
|
|
||||||
fixture.detectChanges()
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
expect(serverServiceMock.getLocalServer).toHaveBeenCalledWith('vps3.gns3.net',3000);
|
|
||||||
expect(router.navigate).toHaveBeenCalledWith(['/server', 99, 'projects']);
|
|
||||||
})
|
|
||||||
service = TestBed.inject(ServerService);
|
|
||||||
}));
|
|
||||||
});
|
|
@ -1,24 +1,24 @@
|
|||||||
<h1 mat-dialog-title>Add server</h1>
|
<h1 mat-dialog-title>Add controller</h1>
|
||||||
<form [formGroup]="serverForm">
|
<form [formGroup]="controllerForm">
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput tabindex="1" formControlName="name" placeholder="Name" />
|
<input matInput tabindex="1" formControlName="name" placeholder="Name" />
|
||||||
<mat-error *ngIf="serverForm.get('name').hasError('required')">You must enter a value</mat-error>
|
<mat-error *ngIf="controllerForm.get('name').hasError('required')">You must enter a value</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field>
|
<!-- <mat-form-field>
|
||||||
<mat-select placeholder="Location" formControlName="location">
|
<mat-select placeholder="Location" formControlName="location">
|
||||||
<mat-option *ngFor="let location of locations" [value]="location.key"> {{ location.name }} </mat-option>
|
<mat-option *ngFor="let location of locations" [value]="location.key"> {{ location.name }} </mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
</mat-form-field> -->
|
||||||
|
|
||||||
|
<!-- <mat-form-field *ngIf="controllerForm.get('location').value === 'local'">
|
||||||
|
<input matInput tabindex="1" formControlName="path" placeholder="Local controller path" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field *ngIf="serverForm.get('location').value === 'local'">
|
<mat-form-field *ngIf="controllerForm.get('location').value === 'local'">
|
||||||
<input matInput tabindex="1" formControlName="path" placeholder="Local server path" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field *ngIf="serverForm.get('location').value === 'local'">
|
|
||||||
<input matInput tabindex="1" formControlName="ubridge_path" placeholder="Ubridge path" />
|
<input matInput tabindex="1" formControlName="ubridge_path" placeholder="Ubridge path" />
|
||||||
</mat-form-field>
|
</mat-form-field> -->
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput tabindex="1" formControlName="host" placeholder="Host" />
|
<input matInput tabindex="1" formControlName="host" placeholder="Host" />
|
@ -2,22 +2,22 @@ import { Component, Inject, OnInit } from '@angular/core';
|
|||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { ElectronService } from 'ngx-electron';
|
import { ElectronService } from 'ngx-electron';
|
||||||
import { Server } from '../../../models/server';
|
import { Controller } from '../../../models/controller';
|
||||||
import { ServerService } from '../../../services/server.service';
|
import { ControllerService } from '../../../services/controller.service';
|
||||||
import { ToasterService } from '../../../services/toaster.service';
|
import { ToasterService } from '../../../services/toaster.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-server-dialog',
|
selector: 'app-add-controller-dialog',
|
||||||
templateUrl: 'add-server-dialog.component.html',
|
templateUrl: 'add-controller-dialog.component.html',
|
||||||
})
|
})
|
||||||
export class AddServerDialogComponent implements OnInit {
|
export class AddControllerDialogComponent implements OnInit {
|
||||||
protocols = [
|
protocols = [
|
||||||
{ key: 'http:', name: 'HTTP' },
|
{ key: 'http:', name: 'HTTP' },
|
||||||
{ key: 'https:', name: 'HTTPS' },
|
{ key: 'https:', name: 'HTTPS' },
|
||||||
];
|
];
|
||||||
locations = [];
|
locations = [];
|
||||||
|
|
||||||
serverForm = new FormGroup({
|
controllerForm = new FormGroup({
|
||||||
name: new FormControl('', [Validators.required]),
|
name: new FormControl('', [Validators.required]),
|
||||||
location: new FormControl(''),
|
location: new FormControl(''),
|
||||||
path: new FormControl(''),
|
path: new FormControl(''),
|
||||||
@ -28,18 +28,18 @@ export class AddServerDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<AddServerDialogComponent>,
|
public dialogRef: MatDialogRef<AddControllerDialogComponent>,
|
||||||
private electronService: ElectronService,
|
private electronService: ElectronService,
|
||||||
private serverService: ServerService,
|
private controllerService: ControllerService,
|
||||||
private toasterService: ToasterService,
|
private toasterService: ToasterService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getLocations() {
|
async getLocations() {
|
||||||
const localServers = await this.numberOfLocalServers();
|
const localControllers = await this.numberOfLocalControllers();
|
||||||
|
|
||||||
let locations = [];
|
let locations = [];
|
||||||
if (this.electronService.isElectronApp && localServers === 0) {
|
if (this.electronService.isElectronApp && localControllers === 0) {
|
||||||
locations.push({ key: 'local', name: 'Local' });
|
locations.push({ key: 'local', name: 'Local' });
|
||||||
}
|
}
|
||||||
locations.push({ key: 'remote', name: 'Remote' });
|
locations.push({ key: 'remote', name: 'Remote' });
|
||||||
@ -47,17 +47,17 @@ export class AddServerDialogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultLocation() {
|
async getDefaultLocation() {
|
||||||
const localServers = await this.numberOfLocalServers();
|
const localControllers = await this.numberOfLocalControllers();
|
||||||
if (this.electronService.isElectronApp && localServers === 0) {
|
if (this.electronService.isElectronApp && localControllers === 0) {
|
||||||
return 'local';
|
return 'local';
|
||||||
}
|
}
|
||||||
return 'remote';
|
return 'remote';
|
||||||
}
|
}
|
||||||
|
|
||||||
async numberOfLocalServers() {
|
async numberOfLocalControllers() {
|
||||||
const servers = await this.serverService.findAll();
|
const controllers = await this.controllerService.findAll();
|
||||||
|
|
||||||
return servers.filter((server) => server.location === 'local').length;
|
return controllers.filter((controller) => controller.location === 'local').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultHost() {
|
getDefaultHost() {
|
||||||
@ -68,16 +68,16 @@ export class AddServerDialogComponent implements OnInit {
|
|||||||
return 3080;
|
return 3080;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultLocalServerPath() {
|
async getDefaultLocalControllerPath() {
|
||||||
if (this.electronService.isElectronApp) {
|
if (this.electronService.isElectronApp) {
|
||||||
return await this.electronService.remote.require('./local-server.js').getLocalServerPath();
|
return await this.electronService.remote.require('./local-controller.js').getLocalControllerPath();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultUbridgePath() {
|
async getDefaultUbridgePath() {
|
||||||
if (this.electronService.isElectronApp) {
|
if (this.electronService.isElectronApp) {
|
||||||
return await this.electronService.remote.require('./local-server.js').getUbridgePath();
|
return await this.electronService.remote.require('./local-controller.js').getUbridgePath();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -85,15 +85,15 @@ export class AddServerDialogComponent implements OnInit {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.locations = await this.getLocations();
|
this.locations = await this.getLocations();
|
||||||
|
|
||||||
const defaultLocalServerPath = await this.getDefaultLocalServerPath();
|
const defaultLocalControllerPath = await this.getDefaultLocalControllerPath();
|
||||||
const defaultUbridgePath = await this.getDefaultUbridgePath();
|
const defaultUbridgePath = await this.getDefaultUbridgePath();
|
||||||
|
|
||||||
this.serverForm.get('location').valueChanges.subscribe((location: string) => {
|
this.controllerForm.get('location').valueChanges.subscribe((location: string) => {
|
||||||
const pathControl = this.serverForm.get('path');
|
const pathControl = this.controllerForm.get('path');
|
||||||
const ubridgePathControl = this.serverForm.get('ubridge_path');
|
const ubridgePathControl = this.controllerForm.get('ubridge_path');
|
||||||
|
|
||||||
if (location === 'local') {
|
if (location === 'local') {
|
||||||
pathControl.setValue(defaultLocalServerPath);
|
pathControl.setValue(defaultLocalControllerPath);
|
||||||
pathControl.setValidators([Validators.required]);
|
pathControl.setValidators([Validators.required]);
|
||||||
|
|
||||||
ubridgePathControl.setValue(defaultUbridgePath);
|
ubridgePathControl.setValue(defaultUbridgePath);
|
||||||
@ -114,29 +114,29 @@ export class AddServerDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const defaultLocation = await this.getDefaultLocation();
|
const defaultLocation = await this.getDefaultLocation();
|
||||||
this.serverForm.get('location').setValue(defaultLocation);
|
this.controllerForm.get('location').setValue(defaultLocation);
|
||||||
this.serverForm.get('host').setValue(this.getDefaultHost());
|
this.controllerForm.get('host').setValue(this.getDefaultHost());
|
||||||
this.serverForm.get('port').setValue(this.getDefaultPort());
|
this.controllerForm.get('port').setValue(this.getDefaultPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddClick(): void {
|
onAddClick(): void {
|
||||||
if (!this.serverForm.valid) {
|
if (!this.controllerForm.valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const server: Server = Object.assign({}, this.serverForm.value);
|
const controller:Controller = Object.assign({}, this.controllerForm.value);
|
||||||
this.serverService.checkServerVersion(server).subscribe(
|
this.controllerService.checkControllerVersion(controller).subscribe(
|
||||||
(serverInfo) => {
|
(controllerInfo) => {
|
||||||
if (serverInfo.version.split('.')[0] >= 3) {
|
if (controllerInfo.version.split('.')[0] >= 3) {
|
||||||
this.dialogRef.close(server);
|
this.dialogRef.close(controller);
|
||||||
this.toasterService.success(`Server ${server.name} added.`);
|
this.toasterService.success(`Controller ${controller.name} added.`);
|
||||||
} else {
|
} else {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
this.toasterService.error(`Server version is not supported.`);
|
this.toasterService.error(`Controller version is not supported.`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.toasterService.error('Cannot connect to the server: ' + error);
|
this.toasterService.error('Cannot connect to the controller: ' + error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<mat-card class="info" *ngIf="discoveredController">
|
||||||
|
<mat-card-content align="center">
|
||||||
|
We've discovered GNS3 controller on <b>{{ discoveredController?.host }}:{{ discoveredController?.port }}</b
|
||||||
|
>, would you like to add to the list?
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions align="right">
|
||||||
|
<button mat-button color="accent" (click)="ignore(discoveredController)">NO</button>
|
||||||
|
<button mat-button (click)="accept(discoveredController)">YES</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-divider *ngIf="discoveredController"></mat-divider>
|
@ -0,0 +1,171 @@
|
|||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import{ Controller } from '../../../models/controller';
|
||||||
|
import { Version } from '../../../models/version';
|
||||||
|
import { ControllerDatabase } from '../../../services/controller.database';
|
||||||
|
import { ControllerService } from '../../../services/controller.service';
|
||||||
|
import { MockedControllerService } from '../../../services/controller.service.spec';
|
||||||
|
import { VersionService } from '../../../services/version.service';
|
||||||
|
import { MockedVersionService } from '../../../services/version.service.spec';
|
||||||
|
import { ControllerDiscoveryComponent } from './controller-discovery.component';
|
||||||
|
|
||||||
|
xdescribe('ControllerDiscoveryComponent', () => {
|
||||||
|
let component: ControllerDiscoveryComponent;
|
||||||
|
let fixture: ComponentFixture<ControllerDiscoveryComponent>;
|
||||||
|
let mockedVersionService: MockedVersionService;
|
||||||
|
let mockedControllerService: MockedControllerService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockedControllerService = new MockedControllerService();
|
||||||
|
mockedVersionService = new MockedVersionService();
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MatCardModule, MatDividerModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: VersionService, useFactory: () => mockedVersionService },
|
||||||
|
{ provide: ControllerService, useFactory: () => mockedControllerService },
|
||||||
|
ControllerDatabase,
|
||||||
|
],
|
||||||
|
declarations: [ControllerDiscoveryComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ControllerDiscoveryComponent);
|
||||||
|
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
// we don't really want to run it during testing
|
||||||
|
spyOn(component, 'ngOnInit').and.returnValue(null);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isAvailable', () => {
|
||||||
|
it('should return controller object when controller is available', () => {
|
||||||
|
const version = new Version();
|
||||||
|
version.version = '2.1.8';
|
||||||
|
|
||||||
|
const getVersionSpy = spyOn(mockedVersionService, 'get').and.returnValue(Observable.of(version));
|
||||||
|
|
||||||
|
component.isControllerAvailable('127.0.0.1', 3080).subscribe((s) => {
|
||||||
|
expect(s.host).toEqual('127.0.0.1');
|
||||||
|
expect(s.port).toEqual(3080);
|
||||||
|
});
|
||||||
|
|
||||||
|
const controller = new Controller ();
|
||||||
|
controller.host = '127.0.0.1';
|
||||||
|
controller.port = 3080;
|
||||||
|
|
||||||
|
expect(getVersionSpy).toHaveBeenCalledWith(controller);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error once controller is not available', () => {
|
||||||
|
const controller = new Controller ();
|
||||||
|
controller.host = '127.0.0.1';
|
||||||
|
controller.port = 3080;
|
||||||
|
|
||||||
|
const getVersionSpy = spyOn(mockedVersionService, 'get').and.returnValue(
|
||||||
|
Observable.throwError(new Error('controller is unavailable'))
|
||||||
|
);
|
||||||
|
let hasExecuted = false;
|
||||||
|
|
||||||
|
component.isControllerAvailable('127.0.0.1', 3080).subscribe(
|
||||||
|
(ver) => { },
|
||||||
|
(err) => {
|
||||||
|
hasExecuted = true;
|
||||||
|
expect(err.toString()).toEqual('Error: controller is unavailable');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getVersionSpy).toHaveBeenCalledWith(controller);
|
||||||
|
expect(hasExecuted).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('discovery', () => {
|
||||||
|
it('should discovery all controllers available', (done) => {
|
||||||
|
const version = new Version();
|
||||||
|
version.version = '2.1.8';
|
||||||
|
|
||||||
|
spyOn(component, 'isControllerAvailable').and.callFake((ip, port) => {
|
||||||
|
const controller = new Controller ();
|
||||||
|
controller.host = ip;
|
||||||
|
controller.port = port;
|
||||||
|
return Observable.of(controller);
|
||||||
|
});
|
||||||
|
|
||||||
|
component.discovery().subscribe((discovered) => {
|
||||||
|
expect(discovered[0].host).toEqual('127.0.0.1');
|
||||||
|
expect(discovered[0].port).toEqual(3080);
|
||||||
|
|
||||||
|
expect(discovered.length).toEqual(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('discoverFirstAvailableController', () => {
|
||||||
|
let controller:Controller ;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
controller = new Controller ();
|
||||||
|
(controller.host = '199.111.111.1'), (controller.port = 3333);
|
||||||
|
|
||||||
|
spyOn(component, 'discovery').and.callFake(() => {
|
||||||
|
return Observable.of([controller]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get first controller from discovered and with no added before', fakeAsync(() => {
|
||||||
|
expect(component.discoveredController).toBeUndefined();
|
||||||
|
component.discoverFirstAvailableController();
|
||||||
|
tick();
|
||||||
|
expect(component.discoveredController.host).toEqual('199.111.111.1');
|
||||||
|
expect(component.discoveredController.port).toEqual(3333);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should get first controller from discovered and with already added', fakeAsync(() => {
|
||||||
|
mockedControllerService.controllers.push(controller);
|
||||||
|
|
||||||
|
expect(component.discoveredController).toBeUndefined();
|
||||||
|
component.discoverFirstAvailableController();
|
||||||
|
tick();
|
||||||
|
expect(component.discoveredController).toBeUndefined();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('accepting and ignoring found controller', () => {
|
||||||
|
let controller:Controller ;
|
||||||
|
beforeEach(() => {
|
||||||
|
controller = new Controller ();
|
||||||
|
(controller.host = '199.111.111.1'), (controller.port = 3333);
|
||||||
|
component.discoveredController = controller;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('accept', () => {
|
||||||
|
it('should add new controller', fakeAsync(() => {
|
||||||
|
component.accept(controller);
|
||||||
|
tick();
|
||||||
|
expect(component.discoveredController).toBeNull();
|
||||||
|
expect(mockedControllerService.controllers[0].host).toEqual('199.111.111.1');
|
||||||
|
expect(mockedControllerService.controllers[0].name).toEqual('199.111.111.1');
|
||||||
|
expect(mockedControllerService.controllers[0].location).toEqual('remote');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ignore', () => {
|
||||||
|
it('should reject controller', fakeAsync(() => {
|
||||||
|
component.ignore(controller);
|
||||||
|
tick();
|
||||||
|
expect(component.discoveredController).toBeNull();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,132 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { forkJoin, from } from 'rxjs';
|
||||||
|
import { map } from 'rxjs//operators';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import { Controller, ControllerProtocol } from '../../../models/controller';
|
||||||
|
import { Version } from '../../../models/version';
|
||||||
|
import { ControllerDatabase } from '../../../services/controller.database';
|
||||||
|
import { ControllerService } from '../../../services/controller.service';
|
||||||
|
import { VersionService } from '../../../services/version.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-controller-discovery',
|
||||||
|
templateUrl: './controller-discovery.component.html',
|
||||||
|
styleUrls: ['./controller-discovery.component.scss'],
|
||||||
|
})
|
||||||
|
export class ControllerDiscoveryComponent implements OnInit {
|
||||||
|
private defaultControllers = [
|
||||||
|
{
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 3080,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
discoveredController: Controller;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private versionService: VersionService,
|
||||||
|
private controllerService: ControllerService,
|
||||||
|
private controllerDatabase: ControllerDatabase,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this.controllerService.isServiceInitialized) this.discoverFirstController();
|
||||||
|
this.controllerService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
this.discoverFirstController();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async discoverFirstController() {
|
||||||
|
let discovered = await this.discoverControllers();
|
||||||
|
let local = await this.controllerService.findAll();
|
||||||
|
|
||||||
|
local.forEach((added) => {
|
||||||
|
discovered = discovered.filter((controller) => {
|
||||||
|
return !(controller.host == added.host && controller.port == added.port);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (discovered.length > 0) {
|
||||||
|
this.discoveredController = discovered.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async discoverControllers() {
|
||||||
|
let discoveredControllers: Controller[] = [];
|
||||||
|
this.defaultControllers.forEach(async (testController) => {
|
||||||
|
const controller = new Controller();
|
||||||
|
controller.host = testController.host;
|
||||||
|
controller.port = testController.port;
|
||||||
|
let version = await this.versionService
|
||||||
|
.get(controller)
|
||||||
|
.toPromise()
|
||||||
|
.catch((error) => null);
|
||||||
|
if (version) discoveredControllers.push(controller);
|
||||||
|
});
|
||||||
|
return discoveredControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
discoverFirstAvailableController() {
|
||||||
|
forkJoin([from(this.controllerService.findAll()).pipe(map((s: Controller[]) => s)), this.discovery()]).subscribe(
|
||||||
|
([local, discovered]) => {
|
||||||
|
local.forEach((added) => {
|
||||||
|
discovered = discovered.filter((controller) => {
|
||||||
|
return !(controller.host == added.host && controller.port == added.port);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (discovered.length > 0) {
|
||||||
|
this.discoveredController = discovered.shift();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
discovery(): Observable<Controller[]> {
|
||||||
|
const queries: Observable<Controller>[] = [];
|
||||||
|
|
||||||
|
this.defaultControllers.forEach((testController) => {
|
||||||
|
queries.push(
|
||||||
|
this.isControllerAvailable(testController.host, testController.port).catch((err) => {
|
||||||
|
return Observable.of(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Observable<Controller[]>((observer) => {
|
||||||
|
forkJoin(queries).subscribe((discoveredControllers) => {
|
||||||
|
observer.next(discoveredControllers.filter((s) => s != null));
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isControllerAvailable(ip: string, port: number): Observable<Controller> {
|
||||||
|
const controller = new Controller();
|
||||||
|
controller.host = ip;
|
||||||
|
controller.port = port;
|
||||||
|
return this.versionService.get(controller).flatMap((version: Version) => Observable.of(controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore(controller: Controller) {
|
||||||
|
this.discoveredController = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept(controller: Controller) {
|
||||||
|
if (controller.name == null) {
|
||||||
|
controller.name = controller.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.location = 'remote';
|
||||||
|
controller.protocol = location.protocol as ControllerProtocol;
|
||||||
|
|
||||||
|
this.controllerService.create(controller).then((created: Controller) => {
|
||||||
|
this.controllerDatabase.addController(created);
|
||||||
|
this.discoveredController = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="default-header"><h1>Servers</h1></div>
|
<div class="default-header"><h1>Controllers</h1></div>
|
||||||
<div class="default-content">
|
<div class="default-content">
|
||||||
<app-server-discovery></app-server-discovery>
|
<app-controller-discovery></app-controller-discovery>
|
||||||
|
|
||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<mat-table #table [dataSource]="dataSource">
|
<mat-table #table [dataSource]="dataSource">
|
||||||
@ -14,13 +14,13 @@
|
|||||||
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let row">
|
<mat-cell *matCellDef="let row">
|
||||||
<a
|
<a
|
||||||
*ngIf="getServerStatus(row) === 'running' || row.location === 'remote' || row.location === 'bundled'"
|
*ngIf="getControllerStatus(row) === 'running' || row.location === 'remote' || row.location === 'bundled'"
|
||||||
[routerLink]="['/server', row.id, 'login']"
|
[routerLink]="['/controller', row.id, 'login']"
|
||||||
class="table-link"
|
class="table-link"
|
||||||
>{{ row.name }}</a
|
>{{ row.name }}</a
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
*ngIf="getServerStatus(row) != 'running' && row.location !== 'remote' && row.location !== 'bundled'"
|
*ngIf="getControllerStatus(row) != 'running' && row.location !== 'remote' && row.location !== 'bundled'"
|
||||||
>{{ row.name }}</span
|
>{{ row.name }}</span
|
||||||
>
|
>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
@ -49,43 +49,43 @@
|
|||||||
matTooltip="Go to projects"
|
matTooltip="Go to projects"
|
||||||
matTooltipClass="custom-tooltip"
|
matTooltipClass="custom-tooltip"
|
||||||
(click)="openProjects(row)"
|
(click)="openProjects(row)"
|
||||||
*ngIf="getServerStatus(row) === 'running' || row.location === 'remote' || row.location === 'bundled'"
|
*ngIf="getControllerStatus(row) === 'running' || row.location === 'remote' || row.location === 'bundled'"
|
||||||
>
|
>
|
||||||
<mat-icon aria-label="Go to projects">arrow_forward</mat-icon>
|
<mat-icon aria-label="Go to projects">arrow_forward</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
matTooltip="Start server"
|
matTooltip="Start controller"
|
||||||
matTooltipClass="custom-tooltip"
|
matTooltipClass="custom-tooltip"
|
||||||
(click)="startServer(row)"
|
(click)="startController(row)"
|
||||||
*ngIf="row.location === 'local' && getServerStatus(row) === 'stopped'"
|
*ngIf="row.location === 'local' && getControllerStatus(row) === 'stopped'"
|
||||||
>
|
>
|
||||||
<mat-icon aria-label="Start server">play_arrow</mat-icon>
|
<mat-icon aria-label="Start controller">play_arrow</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
matTooltip="Stop server"
|
matTooltip="Stop controller"
|
||||||
matTooltipClass="custom-tooltip"
|
matTooltipClass="custom-tooltip"
|
||||||
(click)="stopServer(row)"
|
(click)="stopController(row)"
|
||||||
*ngIf="row.location === 'local' && getServerStatus(row) === 'running'"
|
*ngIf="row.location === 'local' && getControllerStatus(row) === 'running'"
|
||||||
>
|
>
|
||||||
<mat-icon aria-label="Stop server">stop</mat-icon>
|
<mat-icon aria-label="Stop controller">stop</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-spinner
|
<mat-spinner
|
||||||
[diameter]="24"
|
[diameter]="24"
|
||||||
*ngIf="row.location === 'local' && getServerStatus(row) === 'starting'"
|
*ngIf="row.location === 'local' && getControllerStatus(row) === 'starting'"
|
||||||
></mat-spinner>
|
></mat-spinner>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
matTooltip="Remove server"
|
matTooltip="Remove controller"
|
||||||
matTooltipClass="custom-tooltip"
|
matTooltipClass="custom-tooltip"
|
||||||
(click)="deleteServer(row)"
|
(click)="deleteController(row)"
|
||||||
>
|
>
|
||||||
<mat-icon aria-label="Remove server">delete</mat-icon>
|
<mat-icon aria-label="Remove controller">delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -96,10 +96,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttons-bar">
|
<div class="buttons-bar">
|
||||||
<button *ngIf="isElectronApp" mat-raised-button class="button" (click)="startLocalServer()">
|
<button *ngIf="isElectronApp" mat-raised-button class="button" (click)="startLocalController()">
|
||||||
Start local server
|
Start local controller
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button class="button" color="primary" (click)="createModal()">Add server</button>
|
<button mat-raised-button class="button" color="primary" (click)="createModal()">Add Controller</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
14
src/app/components/controllers/controllers.component.scss
Normal file
14
src/app/components/controllers/controllers.component.scss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.buttons-bar {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: 20px !important;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-header-cell, mat-cell {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
@ -4,11 +4,11 @@ import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
|||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { ServerDatabase } from '../../services/server.database';
|
import { ControllerDatabase } from '../../services/controller.database';
|
||||||
import { ServerService } from '../../services/server.service';
|
import { ControllerService } from '../../services/controller.service';
|
||||||
import { MockedServerService } from 'app/services/server.service.spec';
|
import { MockedControllerService } from 'app/services/controller.service.spec';
|
||||||
import { ServersComponent } from './servers.component';
|
import { ControllersComponent } from './controllers.component';
|
||||||
import { ServerManagementService } from 'app/services/server-management.service';
|
import { ControllerManagementService } from '../../services/controller-management.service';
|
||||||
import { ElectronService } from 'ngx-electron';
|
import { ElectronService } from 'ngx-electron';
|
||||||
import { ChildProcessService } from 'ngx-childprocess';
|
import { ChildProcessService } from 'ngx-childprocess';
|
||||||
import { MatBottomSheet, MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
import { MatBottomSheet, MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
||||||
@ -18,16 +18,16 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { ChangeDetectorRef } from '@angular/core';
|
import { ChangeDetectorRef } from '@angular/core';
|
||||||
import { MockedRouter } from 'app/common/progress/progress.component.spec';
|
import { MockedRouter } from 'app/common/progress/progress.component.spec';
|
||||||
|
|
||||||
describe('ServersComponent', () => {
|
describe('ControllersComponent', () => {
|
||||||
let component: ServersComponent;
|
let component: ControllersComponent;
|
||||||
let fixture: ComponentFixture<ServersComponent>;
|
let fixture: ComponentFixture<ControllersComponent>;
|
||||||
let serverMockedService: MockedServerService
|
let controllerMockedService: MockedControllerService
|
||||||
let mockedActivatedRoute: MockedActivatedRoute
|
let mockedActivatedRoute: MockedActivatedRoute
|
||||||
let mockedRouter : MockedRouter
|
let mockedRouter : MockedRouter
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ServersComponent],
|
declarations: [ControllersComponent],
|
||||||
imports: [
|
imports: [
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
RouterTestingModule,
|
RouterTestingModule,
|
||||||
@ -35,13 +35,13 @@ describe('ServersComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
MatDialog,
|
MatDialog,
|
||||||
ServerDatabase,
|
ControllerDatabase,
|
||||||
ServerManagementService,
|
ControllerManagementService,
|
||||||
ElectronService,
|
ElectronService,
|
||||||
MatBottomSheet,
|
MatBottomSheet,
|
||||||
ChildProcessService,
|
ChildProcessService,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
{ provide: ServerService, useValue: serverMockedService },
|
{ provide: ControllerService, useValue: controllerMockedService },
|
||||||
{ provide: ActivatedRoute, useValue: mockedActivatedRoute },
|
{ provide: ActivatedRoute, useValue: mockedActivatedRoute },
|
||||||
{ provide: Router, useValue: mockedRouter },
|
{ provide: Router, useValue: mockedRouter },
|
||||||
]
|
]
|
||||||
@ -49,7 +49,7 @@ describe('ServersComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ServersComponent);
|
fixture = TestBed.createComponent(ControllersComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
177
src/app/components/controllers/controllers.component.ts
Normal file
177
src/app/components/controllers/controllers.component.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import { DataSource } from '@angular/cdk/collections';
|
||||||
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { ChildProcessService } from 'ngx-childprocess';
|
||||||
|
import { ElectronService } from 'ngx-electron';
|
||||||
|
import { merge, Observable, Subscription } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import {Controller , ControllerProtocol } from '../../models/controller';
|
||||||
|
import { ControllerManagementService } from '../../services/controller-management.service';
|
||||||
|
import { ControllerDatabase } from '../../services/controller.database';
|
||||||
|
import { ControllerService } from '../../services/controller.service';
|
||||||
|
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
|
||||||
|
import { AddControllerDialogComponent } from './add-controller-dialog/add-controller-dialog.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-controller-list',
|
||||||
|
templateUrl: './controllers.component.html',
|
||||||
|
styleUrls: ['./controllers.component.scss'],
|
||||||
|
})
|
||||||
|
export class ControllersComponent implements OnInit, OnDestroy {
|
||||||
|
dataSource: ControllerDataSource;
|
||||||
|
displayedColumns = ['id', 'name', 'location', 'ip', 'port', 'actions'];
|
||||||
|
controllerStatusSubscription: Subscription;
|
||||||
|
isElectronApp: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private controllerService: ControllerService,
|
||||||
|
private controllerDatabase: ControllerDatabase,
|
||||||
|
private controllerManagement: ControllerManagementService,
|
||||||
|
private changeDetector: ChangeDetectorRef,
|
||||||
|
private electronService: ElectronService,
|
||||||
|
private childProcessService: ChildProcessService,
|
||||||
|
private bottomSheet: MatBottomSheet,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
|
) { }
|
||||||
|
|
||||||
|
getControllers() {
|
||||||
|
const runningControllerNames = this.controllerManagement.getRunningControllers();
|
||||||
|
|
||||||
|
this.controllerService.findAll().then((controllers:Controller []) => {
|
||||||
|
controllers.forEach((controller) => {
|
||||||
|
const controllerIndex = runningControllerNames.findIndex((controllerName) => controller.name === controllerName);
|
||||||
|
if (controllerIndex >= 0) {
|
||||||
|
controller.status = 'running';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
controllers.forEach((controller) => {
|
||||||
|
this.controllerService.checkControllerVersion(controller).subscribe(
|
||||||
|
(controllerInfo) => {
|
||||||
|
if (controllerInfo.version.split('.')[0] >= 3) {
|
||||||
|
if (!controller.protocol) controller.protocol = location.protocol as ControllerProtocol;
|
||||||
|
if (!this.controllerDatabase.find(controller.name)) this.controllerDatabase.addController(controller);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => { }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isElectronApp = this.electronService.isElectronApp;
|
||||||
|
|
||||||
|
if (this.controllerService && this.controllerService.isServiceInitialized) this.getControllers();
|
||||||
|
|
||||||
|
if (this.controllerService && this.controllerService.isServiceInitialized) {
|
||||||
|
this.controllerService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
this.getControllers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource = new ControllerDataSource(this.controllerDatabase);
|
||||||
|
|
||||||
|
this.controllerStatusSubscription = this.controllerManagement.controllerStatusChanged.subscribe((controllerStatus) => {
|
||||||
|
const controller = this.controllerDatabase.find(controllerStatus.controllerName);
|
||||||
|
if (!controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (controllerStatus.status === 'starting') {
|
||||||
|
controller.status = 'starting';
|
||||||
|
}
|
||||||
|
if (controllerStatus.status === 'stopped') {
|
||||||
|
controller.status = 'stopped';
|
||||||
|
}
|
||||||
|
if (controllerStatus.status === 'errored') {
|
||||||
|
controller.status = 'stopped';
|
||||||
|
}
|
||||||
|
if (controllerStatus.status === 'started') {
|
||||||
|
controller.status = 'running';
|
||||||
|
}
|
||||||
|
this.controllerDatabase.update(controller);
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.controllerStatusSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
startLocalController() {
|
||||||
|
const controller = this.controllerDatabase.data.find((n) => n.location === 'bundled' || 'local');
|
||||||
|
this.startController(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
openProjects(controller) {
|
||||||
|
this.router.navigate(['/controller', controller.id, 'projects']);
|
||||||
|
}
|
||||||
|
|
||||||
|
createModal() {
|
||||||
|
const dialogRef = this.dialog.open(AddControllerDialogComponent, {
|
||||||
|
width: '350px',
|
||||||
|
autoFocus: false,
|
||||||
|
disableClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe((controller) => {
|
||||||
|
if (controller) {
|
||||||
|
this.controllerService.create(controller).then((created:Controller ) => {
|
||||||
|
this.controllerDatabase.addController(created);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getControllerStatus(controller:Controller ) {
|
||||||
|
if (controller.location === 'local') {
|
||||||
|
if (controller.status === undefined) {
|
||||||
|
return 'stopped';
|
||||||
|
}
|
||||||
|
return controller.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteController(controller:Controller ) {
|
||||||
|
this.bottomSheet.open(ConfirmationBottomSheetComponent);
|
||||||
|
let bottomSheetRef = this.bottomSheet._openedBottomSheetRef;
|
||||||
|
bottomSheetRef.instance.message = 'Do you want to delete the controller?';
|
||||||
|
const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => {
|
||||||
|
if (result) {
|
||||||
|
this.controllerService.delete(controller).then(() => {
|
||||||
|
this.controllerDatabase.remove(controller);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async startController(controller:Controller ) {
|
||||||
|
await this.controllerManagement.start(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopController(controller:Controller ) {
|
||||||
|
await this.controllerManagement.stop(controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ControllerDataSource extends DataSource<Controller> {
|
||||||
|
constructor(private controllerDatabase: ControllerDatabase) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(): Observable< Controller[] > {
|
||||||
|
return merge(this.controllerDatabase.dataChange).pipe(
|
||||||
|
map(() => {
|
||||||
|
return this.controllerDatabase.data;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() { }
|
||||||
|
}
|
@ -1,23 +1,23 @@
|
|||||||
<div [hidden]="!serverOptionsVisibility" class="content">
|
<div [hidden]="!controllerOptionsVisibility" class="content">
|
||||||
<div class="default-header">
|
<div class="default-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h1 class="col">Add new server</h1>
|
<h1 class="col">Add new controller</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="default-content">
|
<div class="default-content">
|
||||||
<mat-card class="matCard">
|
<mat-card class="matCard">
|
||||||
<form [formGroup]="serverForm">
|
<form [formGroup]="controllerForm">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput tabindex="1" formControlName="name" placeholder="Name" />
|
<input matInput tabindex="1" formControlName="name" placeholder="Name" />
|
||||||
<mat-error *ngIf="serverForm.get('name').hasError('required')">You must enter a value</mat-error>
|
<mat-error *ngIf="controllerForm.get('name').hasError('required')">You must enter a value</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select placeholder="Location" formControlName="location">
|
<mat-select placeholder="Location" formControlName="location">
|
||||||
<mat-option *ngFor="let location of locations" [value]="location.key"> {{ location.name }} </mat-option>
|
<mat-option *ngFor="let location of locations" [value]="location.key"> {{ location.name }} </mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select placeholder="Protocol" formControlName="protocol">
|
<mat-select placeholder="Protocol" formControlName="protocol">
|
||||||
<mat-option *ngFor="let protocol of protocols" [value]="protocol.key"> {{ protocol.name }} </mat-option>
|
<mat-option *ngFor="let protocol of protocols" [value]="protocol.key"> {{ protocol.name }} </mat-option>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
<div class="buttons-bar">
|
<div class="buttons-bar">
|
||||||
<button mat-raised-button color="primary" (click)="createServer()">Add server</button>
|
<button mat-raised-button color="primary" (click)="createController()">Add controller</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
import { ServerDatabase } from '../../services/server.database';
|
import { ControllerDatabase } from '../../services/controller.database';
|
||||||
import { ServerService } from '../../services/server.service';
|
import { ControllerService } from '../../services/controller.service';
|
||||||
import { ToasterService } from '../../services/toaster.service';
|
import { ToasterService } from '../../services/toaster.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -13,9 +13,9 @@ import { ToasterService } from '../../services/toaster.service';
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class DirectLinkComponent implements OnInit {
|
export class DirectLinkComponent implements OnInit {
|
||||||
public serverOptionsVisibility = false;
|
public controllerOptionsVisibility = false;
|
||||||
public serverIp;
|
public controllerIp;
|
||||||
public serverPort;
|
public controllerPort;
|
||||||
public projectId;
|
public projectId;
|
||||||
|
|
||||||
protocols = [
|
protocols = [
|
||||||
@ -27,61 +27,61 @@ export class DirectLinkComponent implements OnInit {
|
|||||||
{ key: 'remote', name: 'Remote' },
|
{ key: 'remote', name: 'Remote' },
|
||||||
];
|
];
|
||||||
|
|
||||||
serverForm = new FormGroup({
|
controllerForm = new FormGroup({
|
||||||
name: new FormControl('', [Validators.required]),
|
name: new FormControl('', [Validators.required]),
|
||||||
location: new FormControl(''),
|
location: new FormControl(''),
|
||||||
protocol: new FormControl('http:')
|
protocol: new FormControl('http:')
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private serverService: ServerService,
|
private controllerService: ControllerService,
|
||||||
private serverDatabase: ServerDatabase,
|
private controllerDatabase: ControllerDatabase,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private toasterService: ToasterService
|
private toasterService: ToasterService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.serverService.isServiceInitialized) this.getServers();
|
if (this.controllerService.isServiceInitialized) this.getControllers();
|
||||||
|
|
||||||
this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
|
this.controllerService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.getServers();
|
this.getControllers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getServers() {
|
private async getControllers() {
|
||||||
this.serverIp = this.route.snapshot.paramMap.get('server_ip');
|
this.controllerIp = this.route.snapshot.paramMap.get('controller_ip');
|
||||||
this.serverPort = +this.route.snapshot.paramMap.get('server_port');
|
this.controllerPort = +this.route.snapshot.paramMap.get('controller_port');
|
||||||
this.projectId = this.route.snapshot.paramMap.get('project_id');
|
this.projectId = this.route.snapshot.paramMap.get('project_id');
|
||||||
|
|
||||||
const servers = await this.serverService.findAll();
|
const controllers = await this.controllerService.findAll();
|
||||||
const server = servers.filter((server) => server.host === this.serverIp && server.port === this.serverPort)[0];
|
const controller = controllers.filter((controller) => controller.host === this.controllerIp && controller.port === this.controllerPort)[0];
|
||||||
|
|
||||||
if (server) {
|
if (controller) {
|
||||||
this.router.navigate(['/server', server.id, 'project', this.projectId]);
|
this.router.navigate(['/controller', controller.id, 'project', this.projectId]);
|
||||||
} else {
|
} else {
|
||||||
this.serverOptionsVisibility = true;
|
this.controllerOptionsVisibility = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public createServer() {
|
public createController() {
|
||||||
if (!this.serverForm.get('name').hasError && !this.serverForm.get('location').hasError && !this.serverForm.get('protocol').hasError) {
|
if (!this.controllerForm.get('name').hasError && !this.controllerForm.get('location').hasError && !this.controllerForm.get('protocol').hasError) {
|
||||||
this.toasterService.error('Please use correct values');
|
this.toasterService.error('Please use correct values');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverToAdd: Server = new Server();
|
let controllerToAdd:Controller = new Controller ();
|
||||||
serverToAdd.host = this.serverIp;
|
controllerToAdd.host = this.controllerIp;
|
||||||
serverToAdd.port = this.serverPort;
|
controllerToAdd.port = this.controllerPort;
|
||||||
|
|
||||||
serverToAdd.name = this.serverForm.get('name').value;
|
controllerToAdd.name = this.controllerForm.get('name').value;
|
||||||
serverToAdd.location = this.serverForm.get('location').value;
|
controllerToAdd.location = this.controllerForm.get('location').value;
|
||||||
serverToAdd.protocol = this.serverForm.get('protocol').value;
|
controllerToAdd.protocol = this.controllerForm.get('protocol').value;
|
||||||
|
|
||||||
this.serverService.create(serverToAdd).then((addedServer: Server) => {
|
this.controllerService.create(controllerToAdd).then((addedController:Controller ) => {
|
||||||
this.router.navigate(['/server', addedServer.id, 'project', this.projectId]);
|
this.router.navigate(['/controller', addedController.id, 'project', this.projectId]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { AddedDataEvent } from '../../../cartography/events/event-source';
|
|||||||
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
||||||
import { Drawing } from '../../../cartography/models/drawing';
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { DrawingService } from '../../../services/drawing.service';
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,7 +16,7 @@ import { DrawingService } from '../../../services/drawing.service';
|
|||||||
styleUrls: ['./drawing-added.component.scss'],
|
styleUrls: ['./drawing-added.component.scss'],
|
||||||
})
|
})
|
||||||
export class DrawingAddedComponent implements OnInit, OnDestroy {
|
export class DrawingAddedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller: Controller
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
@Input() selectedDrawing: string;
|
@Input() selectedDrawing: string;
|
||||||
@Output() drawingSaved = new EventEmitter<boolean>();
|
@Output() drawingSaved = new EventEmitter<boolean>();
|
||||||
@ -49,9 +49,9 @@ export class DrawingAddedComponent implements OnInit, OnDestroy {
|
|||||||
let svgText = this.mapDrawingToSvgConverter.convert(drawing);
|
let svgText = this.mapDrawingToSvgConverter.convert(drawing);
|
||||||
|
|
||||||
this.drawingService
|
this.drawingService
|
||||||
.add(this.server, this.project.project_id, evt.x, evt.y, svgText)
|
.add(this.controller, this.project.project_id, evt.x, evt.y, svgText)
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
.subscribe((controllerDrawing: Drawing) => {
|
||||||
this.drawingsDataSource.add(serverDrawing);
|
this.drawingsDataSource.add(controllerDrawing);
|
||||||
this.drawingSaved.emit(true);
|
this.drawingSaved.emit(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
|||||||
import { Drawing } from '../../../cartography/models/drawing';
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { DrawingService } from '../../../services/drawing.service';
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,7 +15,7 @@ import { DrawingService } from '../../../services/drawing.service';
|
|||||||
styleUrls: ['./drawing-dragged.component.scss'],
|
styleUrls: ['./drawing-dragged.component.scss'],
|
||||||
})
|
})
|
||||||
export class DrawingDraggedComponent implements OnInit, OnDestroy {
|
export class DrawingDraggedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
private drawingDragged: Subscription;
|
private drawingDragged: Subscription;
|
||||||
|
|
||||||
@ -35,9 +35,9 @@ export class DrawingDraggedComponent implements OnInit, OnDestroy {
|
|||||||
drawing.y += draggedEvent.dy;
|
drawing.y += draggedEvent.dy;
|
||||||
|
|
||||||
this.drawingService
|
this.drawingService
|
||||||
.updatePosition(this.server, this.project, drawing, drawing.x, drawing.y)
|
.updatePosition(this.controller, this.project, drawing, drawing.x, drawing.y)
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
.subscribe((controllerDrawing: Drawing) => {
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
this.drawingsDataSource.update(controllerDrawing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { DrawingsEventSource } from '../../../cartography/events/drawings-event-
|
|||||||
import { ResizedDataEvent } from '../../../cartography/events/event-source';
|
import { ResizedDataEvent } from '../../../cartography/events/event-source';
|
||||||
import { Drawing } from '../../../cartography/models/drawing';
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { DrawingService } from '../../../services/drawing.service';
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,7 +15,7 @@ import { DrawingService } from '../../../services/drawing.service';
|
|||||||
styleUrls: ['./drawing-resized.component.scss'],
|
styleUrls: ['./drawing-resized.component.scss'],
|
||||||
})
|
})
|
||||||
export class DrawingResizedComponent implements OnInit, OnDestroy {
|
export class DrawingResizedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
private drawingResized: Subscription;
|
private drawingResized: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -34,9 +34,9 @@ export class DrawingResizedComponent implements OnInit, OnDestroy {
|
|||||||
let svgString = this.mapDrawingToSvgConverter.convert(resizedEvent.datum);
|
let svgString = this.mapDrawingToSvgConverter.convert(resizedEvent.datum);
|
||||||
|
|
||||||
this.drawingService
|
this.drawingService
|
||||||
.updateSizeAndPosition(this.server, drawing, resizedEvent.x, resizedEvent.y, svgString)
|
.updateSizeAndPosition(this.controller, drawing, resizedEvent.x, resizedEvent.y, svgString)
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
.subscribe((controllerDrawing: Drawing) => {
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
this.drawingsDataSource.update(controllerDrawing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
|||||||
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
import { MapLinkNode } from '../../../cartography/models/map/map-link-node';
|
import { MapLinkNode } from '../../../cartography/models/map/map-link-node';
|
||||||
import { Link } from '../../../models/link';
|
import { Link } from '../../../models/link';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { LinkService } from '../../../services/link.service';
|
import { LinkService } from '../../../services/link.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,7 +14,7 @@ import { LinkService } from '../../../services/link.service';
|
|||||||
styleUrls: ['./interface-label-dragged.component.scss'],
|
styleUrls: ['./interface-label-dragged.component.scss'],
|
||||||
})
|
})
|
||||||
export class InterfaceLabelDraggedComponent {
|
export class InterfaceLabelDraggedComponent {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
private interfaceDragged: Subscription;
|
private interfaceDragged: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -40,8 +40,8 @@ export class InterfaceLabelDraggedComponent {
|
|||||||
link.nodes[1].label.y += draggedEvent.dy;
|
link.nodes[1].label.y += draggedEvent.dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.linkService.updateNodes(this.server, link, link.nodes).subscribe((serverLink: Link) => {
|
this.linkService.updateNodes(this.controller, link, link.nodes).subscribe((controllerLink: Link) => {
|
||||||
this.linksDataSource.update(serverLink);
|
this.linksDataSource.update(controllerLink);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { MapLinkCreated } from '../../../cartography/events/links';
|
|||||||
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
import { Link } from '../../../models/link';
|
import { Link } from '../../../models/link';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { LinkService } from '../../../services/link.service';
|
import { LinkService } from '../../../services/link.service';
|
||||||
import { ProjectService } from '../../../services/project.service';
|
import { ProjectService } from '../../../services/project.service';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ import { ProjectService } from '../../../services/project.service';
|
|||||||
styleUrls: ['./link-created.component.scss'],
|
styleUrls: ['./link-created.component.scss'],
|
||||||
})
|
})
|
||||||
export class LinkCreatedComponent implements OnInit, OnDestroy {
|
export class LinkCreatedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
private linkCreated: Subscription;
|
private linkCreated: Subscription;
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ export class LinkCreatedComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.linkService
|
this.linkService
|
||||||
.createLink(
|
.createLink(
|
||||||
this.server,
|
this.controller,
|
||||||
sourceNode,
|
sourceNode,
|
||||||
sourcePort,
|
sourcePort,
|
||||||
targetNode,
|
targetNode,
|
||||||
@ -98,7 +98,7 @@ export class LinkCreatedComponent implements OnInit, OnDestroy {
|
|||||||
yLabelTargetNode
|
yLabelTargetNode
|
||||||
)
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.projectService.links(this.server, this.project.project_id).subscribe((links: Link[]) => {
|
this.projectService.links(this.controller, this.project.project_id).subscribe((links: Link[]) => {
|
||||||
this.linksDataSource.set(links);
|
this.linksDataSource.set(links);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ import { NodesEventSource } from '../../../cartography/events/nodes-event-source
|
|||||||
import { MapNode } from '../../../cartography/models/map/map-node';
|
import { MapNode } from '../../../cartography/models/map/map-node';
|
||||||
import { Node } from '../../../cartography/models/node';
|
import { Node } from '../../../cartography/models/node';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { NodeService } from '../../../services/node.service';
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,7 +15,7 @@ import { NodeService } from '../../../services/node.service';
|
|||||||
styleUrls: ['./node-dragged.component.scss'],
|
styleUrls: ['./node-dragged.component.scss'],
|
||||||
})
|
})
|
||||||
export class NodeDraggedComponent implements OnInit, OnDestroy {
|
export class NodeDraggedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
private nodeDragged: Subscription;
|
private nodeDragged: Subscription;
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ export class NodeDraggedComponent implements OnInit, OnDestroy {
|
|||||||
node.x += draggedEvent.dx;
|
node.x += draggedEvent.dx;
|
||||||
node.y += draggedEvent.dy;
|
node.y += draggedEvent.dy;
|
||||||
|
|
||||||
this.nodeService.updatePosition(this.server, this.project, node, node.x, node.y).subscribe((serverNode: Node) => {
|
this.nodeService.updatePosition(this.controller, this.project, node, node.x, node.y).subscribe((controllerNode: Node) => {
|
||||||
this.nodesDataSource.update(serverNode);
|
this.nodesDataSource.update(controllerNode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
|||||||
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
||||||
import { MapLabel } from '../../../cartography/models/map/map-label';
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
import { Node } from '../../../cartography/models/node';
|
import { Node } from '../../../cartography/models/node';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { NodeService } from '../../../services/node.service';
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,7 +15,7 @@ import { NodeService } from '../../../services/node.service';
|
|||||||
styleUrls: ['./node-label-dragged.component.scss'],
|
styleUrls: ['./node-label-dragged.component.scss'],
|
||||||
})
|
})
|
||||||
export class NodeLabelDraggedComponent implements OnInit, OnDestroy {
|
export class NodeLabelDraggedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller:Controller ;
|
||||||
private nodeLabelDragged: Subscription;
|
private nodeLabelDragged: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -38,8 +38,8 @@ export class NodeLabelDraggedComponent implements OnInit, OnDestroy {
|
|||||||
const label = this.mapLabelToLabel.convert(mapLabel);
|
const label = this.mapLabelToLabel.convert(mapLabel);
|
||||||
node.label = label;
|
node.label = label;
|
||||||
|
|
||||||
this.nodeService.updateLabel(this.server, node, node.label).subscribe((serverNode: Node) => {
|
this.nodeService.updateLabel(this.controller, node, node.label).subscribe((controllerNode: Node) => {
|
||||||
this.nodesDataSource.update(serverNode);
|
this.nodesDataSource.update(controllerNode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { Context } from '../../../cartography/models/context';
|
|||||||
import { Drawing } from '../../../cartography/models/drawing';
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||||
import { Project } from '../../../models/project';
|
import { Project } from '../../../models/project';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { DrawingService } from '../../../services/drawing.service';
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -18,7 +18,7 @@ import { DrawingService } from '../../../services/drawing.service';
|
|||||||
styleUrls: ['./text-added.component.scss'],
|
styleUrls: ['./text-added.component.scss'],
|
||||||
})
|
})
|
||||||
export class TextAddedComponent implements OnInit, OnDestroy {
|
export class TextAddedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller: Controller;
|
||||||
@Input() project: Project;
|
@Input() project: Project;
|
||||||
@Output() drawingSaved = new EventEmitter<boolean>();
|
@Output() drawingSaved = new EventEmitter<boolean>();
|
||||||
private textAdded: Subscription;
|
private textAdded: Subscription;
|
||||||
@ -43,7 +43,7 @@ export class TextAddedComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.drawingService
|
this.drawingService
|
||||||
.add(
|
.add(
|
||||||
this.server,
|
this.controller,
|
||||||
this.project.project_id,
|
this.project.project_id,
|
||||||
(evt.x - (this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x)) /
|
(evt.x - (this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x)) /
|
||||||
this.context.transformation.k,
|
this.context.transformation.k,
|
||||||
@ -51,8 +51,8 @@ export class TextAddedComponent implements OnInit, OnDestroy {
|
|||||||
this.context.transformation.k,
|
this.context.transformation.k,
|
||||||
svgText
|
svgText
|
||||||
)
|
)
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
.subscribe((controllerDrawing: Drawing) => {
|
||||||
this.drawingsDataSource.add(serverDrawing);
|
this.drawingsDataSource.add(controllerDrawing);
|
||||||
this.drawingSaved.emit(true);
|
this.drawingSaved.emit(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { TextEditedDataEvent } from '../../../cartography/events/event-source';
|
|||||||
import { Drawing } from '../../../cartography/models/drawing';
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||||
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { DrawingService } from '../../../services/drawing.service';
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,7 +16,7 @@ import { DrawingService } from '../../../services/drawing.service';
|
|||||||
styleUrls: ['./text-edited.component.scss'],
|
styleUrls: ['./text-edited.component.scss'],
|
||||||
})
|
})
|
||||||
export class TextEditedComponent implements OnInit, OnDestroy {
|
export class TextEditedComponent implements OnInit, OnDestroy {
|
||||||
@Input() server: Server;
|
@Input() controller: Controller;
|
||||||
private textEdited: Subscription;
|
private textEdited: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -38,8 +38,8 @@ export class TextEditedComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
let drawing = this.drawingsDataSource.get(evt.textDrawingId);
|
let drawing = this.drawingsDataSource.get(evt.textDrawingId);
|
||||||
|
|
||||||
this.drawingService.updateText(this.server, drawing, svgString).subscribe((serverDrawing: Drawing) => {
|
this.drawingService.updateText(this.controller, drawing, svgString).subscribe((controllerDrawing: Drawing) => {
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
this.drawingsDataSource.update(controllerDrawing);
|
||||||
this.drawingsEventSource.textSaved.emit(true);
|
this.drawingsEventSource.textSaved.emit(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<mat-form-field class="input-full-width">
|
<mat-form-field class="input-full-width">
|
||||||
<mat-select formControlName="compression" (selectionChange)="selectCompression($event)" required>
|
<mat-select formControlName="compression" (selectionChange)="selectCompression($event)" required>
|
||||||
<mat-option *ngFor="let compressionValue of compression_methods" [value]="compressionValue">{{
|
<mat-option *ngFor="let compressionValue of compression_methods" [value]="compressionValue">{{
|
||||||
compressionValue.name
|
compressionValue?.name
|
||||||
}}</mat-option>
|
}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@ -2,7 +2,7 @@ import { Component, Inject, OnInit } from '@angular/core';
|
|||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { Project } from '../../models/project';
|
import { Project } from '../../models/project';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
import { ProjectService } from '../../services/project.service';
|
import { ProjectService } from '../../services/project.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,7 +16,7 @@ export class ExportPortableProjectComponent implements OnInit {
|
|||||||
compression_methods: any = [];
|
compression_methods: any = [];
|
||||||
compression_level: any = [];
|
compression_level: any = [];
|
||||||
compression_filter_value: any = [];
|
compression_filter_value: any = [];
|
||||||
server: Server;
|
controller:Controller ;
|
||||||
project: Project;
|
project: Project;
|
||||||
index: number = 4;
|
index: number = 4;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
@ -30,7 +30,7 @@ export class ExportPortableProjectComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.server = this.data.serverDetails;
|
this.controller = this.data.controllerDetails;
|
||||||
this.project = this.data.projectDetails;
|
this.project = this.data.projectDetails;
|
||||||
this.fileName = this.project.name + '.gns3project';
|
this.fileName = this.project.name + '.gns3project';
|
||||||
await this.formControls();
|
await this.formControls();
|
||||||
@ -65,7 +65,7 @@ export class ExportPortableProjectComponent implements OnInit {
|
|||||||
exportPortableProject() {
|
exportPortableProject() {
|
||||||
this.isExport = true;
|
this.isExport = true;
|
||||||
this.export_project_form.value.compression = this.export_project_form.value.compression.value ?? 'zstd';
|
this.export_project_form.value.compression = this.export_project_form.value.compression.value ?? 'zstd';
|
||||||
window.location.assign(this.projectService.getexportPortableProjectPath(this.server, this.project.project_id, this.export_project_form.value))
|
window.location.assign(this.projectService.getexportPortableProjectPath(this.controller, this.project.project_id, this.export_project_form.value))
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
<div class="title">
|
||||||
|
<h3>Add Role To group: {{data.group.name}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<mat-label>Search user </mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="searchText" (keydown)="onSearch()">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="userList" *ngFor="let role of displayedRoles | async">
|
||||||
|
<div>{{role.name}}</div>
|
||||||
|
<mat-icon (click)="addRole(role)" *ngIf="!loading">add</mat-icon>
|
||||||
|
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||||
|
</div>
|
@ -0,0 +1,35 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
display: flex;
|
||||||
|
width: 600px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userList {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-spinner {
|
||||||
|
width: 36px;
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {BehaviorSubject, forkJoin, timer} from "rxjs";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {Controller} from "@models/controller";
|
||||||
|
import {Group} from "@models/groups/group";
|
||||||
|
import {UserService} from "@services/user.service";
|
||||||
|
import {GroupService} from "@services/group.service";
|
||||||
|
import {ToasterService} from "@services/toaster.service";
|
||||||
|
import {Role} from "@models/api/role";
|
||||||
|
import {RoleService} from "@services/role.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-role-to-group',
|
||||||
|
templateUrl: './add-role-to-group.component.html',
|
||||||
|
styleUrls: ['./add-role-to-group.component.scss']
|
||||||
|
})
|
||||||
|
export class AddRoleToGroupComponent implements OnInit {
|
||||||
|
roles = new BehaviorSubject<Role[]>([]);
|
||||||
|
displayedRoles = new BehaviorSubject<Role[]>([]);
|
||||||
|
|
||||||
|
searchText: string;
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialogRef<AddRoleToGroupComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { controller: Controller; group: Group },
|
||||||
|
private groupService: GroupService,
|
||||||
|
private roleService: RoleService,
|
||||||
|
private toastService: ToasterService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getRoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch() {
|
||||||
|
timer(500)
|
||||||
|
.subscribe(() => {
|
||||||
|
const displayedUsers = this.roles.value.filter((roles: Role) => {
|
||||||
|
return roles.name.includes(this.searchText);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.displayedRoles.next(displayedUsers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoles() {
|
||||||
|
forkJoin([
|
||||||
|
this.roleService.get(this.data.controller),
|
||||||
|
this.groupService.getGroupRole(this.data.controller, this.data.group.user_group_id)
|
||||||
|
]).subscribe((results) => {
|
||||||
|
const [globalRoles, groupRoles] = results;
|
||||||
|
const roles = globalRoles.filter((role: Role) => {
|
||||||
|
return !groupRoles.find((r: Role) => r.role_id === role.role_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.roles.next(roles);
|
||||||
|
this.displayedRoles.next(roles);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addRole(role: Role) {
|
||||||
|
this.loading = true;
|
||||||
|
this.groupService
|
||||||
|
.addRoleToGroup(this.data.controller, this.data.group, role)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toastService.success(`role ${role.name} was added`);
|
||||||
|
this.getRoles();
|
||||||
|
this.loading = false;
|
||||||
|
}, (err) => {
|
||||||
|
console.log(err);
|
||||||
|
this.toastService.error(`error while adding role ${role.name} to group ${this.data.group.name}`);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<div class="title">
|
||||||
|
<h3>Add User To group: {{data.group.name}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<mat-label>Search user </mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="searchText" (keydown)="onSearch()">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="userList" *ngFor="let user of displayedUsers | async">
|
||||||
|
<div>{{user.username}}</div>
|
||||||
|
<div>{{user.email}}</div>
|
||||||
|
<mat-icon (click)="addUser(user)" *ngIf="!loading">add</mat-icon>
|
||||||
|
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||||
|
</div>
|
@ -0,0 +1,35 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
display: flex;
|
||||||
|
width: 600px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userList {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-spinner {
|
||||||
|
width: 36px;
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {UserService} from "@services/user.service";
|
||||||
|
import {Controller} from "@models/controller";
|
||||||
|
import {BehaviorSubject, forkJoin, observable, Observable, timer} from "rxjs";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {GroupService} from "@services/group.service";
|
||||||
|
import {Group} from "@models/groups/group";
|
||||||
|
import {tap} from "rxjs/operators";
|
||||||
|
import {ToasterService} from "@services/toaster.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-user-to-group-dialog',
|
||||||
|
templateUrl: './add-user-to-group-dialog.component.html',
|
||||||
|
styleUrls: ['./add-user-to-group-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class AddUserToGroupDialogComponent implements OnInit {
|
||||||
|
users = new BehaviorSubject<User[]>([]);
|
||||||
|
displayedUsers = new BehaviorSubject<User[]>([]);
|
||||||
|
|
||||||
|
searchText: string;
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialogRef<AddUserToGroupDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { controller: Controller; group: Group },
|
||||||
|
private userService: UserService,
|
||||||
|
private groupService: GroupService,
|
||||||
|
private toastService: ToasterService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch() {
|
||||||
|
timer(500)
|
||||||
|
.subscribe(() => {
|
||||||
|
const displayedUsers = this.users.value.filter((user: User) => {
|
||||||
|
return user.username.includes(this.searchText) || user.email?.includes(this.searchText);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.displayedUsers.next(displayedUsers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsers() {
|
||||||
|
forkJoin([
|
||||||
|
this.userService.list(this.data.controller),
|
||||||
|
this.groupService.getGroupMember(this.data.controller, this.data.group.user_group_id)
|
||||||
|
]).subscribe((results) => {
|
||||||
|
const [userList, members] = results;
|
||||||
|
const users = userList.filter((user: User) => {
|
||||||
|
return !members.find((u: User) => u.user_id === user.user_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.users.next(users);
|
||||||
|
this.displayedUsers.next(users);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addUser(user: User) {
|
||||||
|
this.loading = true;
|
||||||
|
this.groupService
|
||||||
|
.addMemberToGroup(this.data.controller, this.data.group, user)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toastService.success(`user ${user.username} was added`);
|
||||||
|
this.getUsers();
|
||||||
|
this.loading = false;
|
||||||
|
}, (err) => {
|
||||||
|
console.log(err);
|
||||||
|
this.toastService.error(`error while adding user ${user.username} to group ${this.data.group.name}`);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
<div class="content">
|
||||||
|
<div class="default-header">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<a
|
||||||
|
mat-icon-button
|
||||||
|
matTooltip="Back to group management"
|
||||||
|
matTooltipClass="custom-tooltip"
|
||||||
|
[routerLink]="['/controller', controller.id, 'management', 'groups']">
|
||||||
|
<mat-icon aria-label="Back to group management">keyboard_arrow_left</mat-icon>
|
||||||
|
</a>
|
||||||
|
<h1 class="col">Groups {{group.name}} details</h1>
|
||||||
|
</div>
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Details" class="details">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Group name:</mat-label>
|
||||||
|
<input matInput type="text" [ngModel]="group.name">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [checked]="group.is_builtin" disabled>Is build in</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="button-div">
|
||||||
|
<button mat-button (click)="onUpdate()" tabindex="2" mat-raised-button color="primary"
|
||||||
|
[disabled]="!editGroupForm.valid">
|
||||||
|
Update Group
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>Creation date: {{group.created_at}}</div>
|
||||||
|
<div>Last update Date: {{group.updated_at}}</div>
|
||||||
|
<div>UUID: {{group.user_group_id}}</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Members">
|
||||||
|
<div class="members">
|
||||||
|
<div>
|
||||||
|
<mat-icon (click)="openAddUserDialog()" class="clickable">person_add</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="filter members" [(ngModel)]="searchMembers"/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div *ngFor="let user of members | membersFilter: searchMembers | paginator: pageEvent">
|
||||||
|
<a href="/controller/{{controller.id}}/management/users/{{user.user_id}}">
|
||||||
|
<div>{{user.username}}</div>
|
||||||
|
</a>
|
||||||
|
<mat-icon class="clickable" (click)="openRemoveUserDialog(user)">delete</mat-icon>
|
||||||
|
</div>
|
||||||
|
<mat-paginator [length]="members.length" (page)="pageEvent = $event"
|
||||||
|
[pageSizeOptions]="[5, 20, 50, 100]"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Roles">
|
||||||
|
<div><button mat-button (click)="openAddRoleDialog()" ><mat-icon>group_add</mat-icon></button></div>
|
||||||
|
<div *ngFor="let role of roles" class="roles">
|
||||||
|
<div>{{role.name}}</div>
|
||||||
|
<div>
|
||||||
|
<button mat-button (click)="openRemoveRoleDialog(role)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,51 @@
|
|||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details > div {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-div {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members > .search {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roles {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
152
src/app/components/group-details/group-details.component.ts
Normal file
152
src/app/components/group-details/group-details.component.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {Controller} from "@models/controller";
|
||||||
|
import {Group} from "@models/groups/group";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {FormControl, FormGroup} from "@angular/forms";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {AddUserToGroupDialogComponent} from "@components/group-details/add-user-to-group-dialog/add-user-to-group-dialog.component";
|
||||||
|
import {RemoveToGroupDialogComponent} from "@components/group-details/remove-to-group-dialog/remove-to-group-dialog.component";
|
||||||
|
import {GroupService} from "@services/group.service";
|
||||||
|
import {ToasterService} from "@services/toaster.service";
|
||||||
|
import {PageEvent} from "@angular/material/paginator";
|
||||||
|
import {Role} from "@models/api/role";
|
||||||
|
import {AddRoleToGroupComponent} from "@components/group-details/add-role-to-group/add-role-to-group.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-group-details',
|
||||||
|
templateUrl: './group-details.component.html',
|
||||||
|
styleUrls: ['./group-details.component.scss']
|
||||||
|
})
|
||||||
|
export class GroupDetailsComponent implements OnInit {
|
||||||
|
controller: Controller;
|
||||||
|
group: Group;
|
||||||
|
members: User[];
|
||||||
|
editGroupForm: FormGroup;
|
||||||
|
pageEvent: PageEvent | undefined;
|
||||||
|
searchMembers: string;
|
||||||
|
roles: Role[];
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private groupService: GroupService,
|
||||||
|
private toastService: ToasterService) {
|
||||||
|
|
||||||
|
this.editGroupForm = new FormGroup({
|
||||||
|
groupname: new FormControl(''),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route.data.subscribe((d: { controller: Controller; group: Group, members: User[], roles: Role[] }) => {
|
||||||
|
|
||||||
|
this.controller = d.controller;
|
||||||
|
this.group = d.group;
|
||||||
|
this.roles = d.roles;
|
||||||
|
this.members = d.members.sort((a: User, b: User) => a.username.toLowerCase().localeCompare(b.username.toLowerCase()));
|
||||||
|
this.editGroupForm.setValue({groupname: this.group.name});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate() {
|
||||||
|
this.groupService.update(this.controller, this.group)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toastService.success(`group updated`);
|
||||||
|
}, (error) => {
|
||||||
|
this.toastService.error('Error: Cannot update group');
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openAddRoleDialog() {
|
||||||
|
this.dialog
|
||||||
|
.open<AddRoleToGroupComponent>(AddRoleToGroupComponent,
|
||||||
|
{
|
||||||
|
width: '700px', height: '500px',
|
||||||
|
data: {controller: this.controller, group: this.group}
|
||||||
|
})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reloadRoles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
openAddUserDialog() {
|
||||||
|
this.dialog
|
||||||
|
.open<AddUserToGroupDialogComponent>(AddUserToGroupDialogComponent,
|
||||||
|
{
|
||||||
|
width: '700px', height: '500px',
|
||||||
|
data: {controller: this.controller, group: this.group}
|
||||||
|
})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reloadMembers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openRemoveUserDialog(user: User) {
|
||||||
|
this.dialog.open<RemoveToGroupDialogComponent>(RemoveToGroupDialogComponent,
|
||||||
|
{width: '500px', height: '200px', data: {name: user.username}})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((confirm: boolean) => {
|
||||||
|
if (confirm) {
|
||||||
|
this.groupService.removeUser(this.controller, this.group, user)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toastService.success(`User ${user.username} was removed`);
|
||||||
|
this.reloadMembers();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.toastService.error(`Error while removing user ${user.username} from ${this.group.name}`);
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
openRemoveRoleDialog(role: Role) {
|
||||||
|
this.dialog.open<RemoveToGroupDialogComponent>(RemoveToGroupDialogComponent,
|
||||||
|
{width: '500px', height: '200px', data: {name: role.name}})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((confirm: string) => {
|
||||||
|
if (confirm) {
|
||||||
|
this.groupService.removeRole(this.controller, this.group, role)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toastService.success(`Role ${role.name} was removed`);
|
||||||
|
this.reloadRoles();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.toastService.error(`Error while removing role ${role.name} from ${this.group.name}`);
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadMembers() {
|
||||||
|
this.groupService.getGroupMember(this.controller, this.group.user_group_id)
|
||||||
|
.subscribe((members: User[]) => {
|
||||||
|
this.members = members;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadRoles() {
|
||||||
|
this.groupService.getGroupRole(this.controller, this.group.user_group_id)
|
||||||
|
.subscribe((roles: Role[]) => {
|
||||||
|
this.roles = roles;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
src/app/components/group-details/members-filter.pipe.ts
Normal file
32
src/app/components/group-details/members-filter.pipe.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'membersFilter'
|
||||||
|
})
|
||||||
|
export class MembersFilterPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(members: User[], filterText: string): User[] {
|
||||||
|
if (!members) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (filterText === undefined || filterText === '') {
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
|
return members.filter((member: User) => member.username.toLowerCase().includes(filterText.toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
src/app/components/group-details/paginator.pipe.ts
Normal file
41
src/app/components/group-details/paginator.pipe.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {PageEvent} from "@angular/material/paginator";
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'paginator'
|
||||||
|
})
|
||||||
|
export class PaginatorPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform<T>(elements: T[] | undefined, paginatorEvent: PageEvent | undefined): T[] {
|
||||||
|
if (!elements) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paginatorEvent) {
|
||||||
|
paginatorEvent = {
|
||||||
|
length: elements.length,
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 5
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return elements.slice(
|
||||||
|
paginatorEvent.pageIndex * paginatorEvent.pageSize,
|
||||||
|
(paginatorEvent.pageIndex + 1) * paginatorEvent.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="header">
|
||||||
|
<div>Confirm ?</div>
|
||||||
|
<div>Removing: {{data.name}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<button mat-button mat-raised-button color="primary" (click)="onCancel()">No, cancel</button>
|
||||||
|
<button mat-button mat-raised-button color="warn"(click)="onConfirm()">Yes, remove</button>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-remove-user-to-group-dialog',
|
||||||
|
templateUrl: './remove-to-group-dialog.component.html',
|
||||||
|
styleUrls: ['./remove-to-group-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class RemoveToGroupDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private dialogRef: MatDialogRef<RemoveToGroupDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { name: string }) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.dialogRef.close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
onConfirm() {
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-remove-user-to-group-dialog',
|
||||||
|
templateUrl: './remove-user-to-group-dialog.component.html',
|
||||||
|
styleUrls: ['./remove-user-to-group-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class RemoveUserToGroupDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private dialogRef: MatDialogRef<RemoveUserToGroupDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { user: User }) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onConfirm() {
|
||||||
|
this.dialogRef.close(this.data.user);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GroupNameValidator {
|
||||||
|
get(groupName) {
|
||||||
|
const pattern = new RegExp(/[~`!#$%\^&*+=\[\]\\';,/{}|\\":<>\?]/);
|
||||||
|
|
||||||
|
if (!pattern.test(groupName.value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { invalidName: true };
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
<h1 mat-dialog-title>Create new group</h1>
|
||||||
|
<form [formGroup]="groupNameForm" class="file-name-form">
|
||||||
|
<mat-form-field class="file-name-form-field">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
(keydown)="onKeyDown($event)"
|
||||||
|
type="text"
|
||||||
|
formControlName="groupName"
|
||||||
|
[ngClass]="{ 'is-invalid': form.groupName?.errors }"
|
||||||
|
placeholder="Please enter group name"
|
||||||
|
/>
|
||||||
|
<mat-error *ngIf="form.groupName?.touched && form.groupName?.errors && form.groupName?.errors.required"
|
||||||
|
>Group name is required</mat-error
|
||||||
|
>
|
||||||
|
<mat-error *ngIf="form.groupName?.errors && form.groupName?.errors.invalidName"
|
||||||
|
>Group name is incorrect</mat-error
|
||||||
|
>
|
||||||
|
<mat-error *ngIf="form.groupName?.errors && form.groupName?.errors.projectExist"
|
||||||
|
>Group with this name exists</mat-error
|
||||||
|
>
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h5>Add users to group: </h5>
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<mat-label>Users</mat-label>
|
||||||
|
<input type="text"
|
||||||
|
matInput
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
[formControl]="autocompleteControl">
|
||||||
|
<mat-autocomplete #auto="matAutocomplete"[displayWith]="displayFn" (optionSelected)='selectedUser($event.option.value)'>
|
||||||
|
<mat-option *ngFor="let user of filteredUsers | async" [value]="user" >
|
||||||
|
{{user.username}} - {{user.email}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="users">
|
||||||
|
<div *ngFor="let user of usersToAdd">
|
||||||
|
<div class="userList">
|
||||||
|
<div>{{user.username}}</div>
|
||||||
|
<div>{{user.email}}</div>
|
||||||
|
<mat-icon (click)="delUser(user)">delete</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div mat-dialog-actions class="button-div">
|
||||||
|
<button mat-button (click)="onNoClick()" color="accent">Cancel</button>
|
||||||
|
<button mat-button (click)="onAddClick()" tabindex="2" class="add-project-button" mat-raised-button color="primary">
|
||||||
|
Add group
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
.file-name-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-snackbar {
|
||||||
|
background: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userList {
|
||||||
|
display: flex;
|
||||||
|
margin: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.users {
|
||||||
|
display: flex;
|
||||||
|
height: 180px;
|
||||||
|
overflow: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-div {
|
||||||
|
float: right;
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||||
|
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
|
||||||
|
import {groupNameAsyncValidator} from "@components/group-management/add-group-dialog/groupNameAsyncValidator";
|
||||||
|
import {GroupNameValidator} from "@components/group-management/add-group-dialog/GroupNameValidator";
|
||||||
|
import {GroupService} from "../../../services/group.service";
|
||||||
|
import {Controller} from "../../../models/controller";
|
||||||
|
import {BehaviorSubject, forkJoin, timer} from "rxjs";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {UserService} from "@services/user.service";
|
||||||
|
import {ToasterService} from "@services/toaster.service";
|
||||||
|
import {PageEvent} from "@angular/material/paginator";
|
||||||
|
import {Observable} from "rxjs/Rx";
|
||||||
|
import {Group} from "@models/groups/group";
|
||||||
|
import {map, startWith} from "rxjs/operators";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-group-dialog',
|
||||||
|
templateUrl: './add-group-dialog.component.html',
|
||||||
|
styleUrls: ['./add-group-dialog.component.scss'],
|
||||||
|
providers: [GroupNameValidator]
|
||||||
|
})
|
||||||
|
export class AddGroupDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
groupNameForm: FormGroup;
|
||||||
|
controller: Controller;
|
||||||
|
|
||||||
|
users: User[];
|
||||||
|
usersToAdd: Set<User> = new Set([]);
|
||||||
|
filteredUsers: Observable<User[]>
|
||||||
|
loading = false;
|
||||||
|
autocompleteControl = new FormControl();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private dialogRef: MatDialogRef<AddGroupDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { controller: Controller },
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private groupNameValidator: GroupNameValidator,
|
||||||
|
private groupService: GroupService,
|
||||||
|
private userService: UserService,
|
||||||
|
private toasterService: ToasterService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.controller = this.data.controller;
|
||||||
|
this.groupNameForm = this.formBuilder.group({
|
||||||
|
groupName: new FormControl(
|
||||||
|
null,
|
||||||
|
[Validators.required, this.groupNameValidator.get],
|
||||||
|
[groupNameAsyncValidator(this.data.controller, this.groupService)]
|
||||||
|
),
|
||||||
|
});
|
||||||
|
this.userService.list(this.controller)
|
||||||
|
.subscribe((users: User[]) => {
|
||||||
|
this.users = users;
|
||||||
|
this.filteredUsers = this.autocompleteControl.valueChanges.pipe(
|
||||||
|
startWith(''),
|
||||||
|
map(value => this._filter(value)),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
this.onAddClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get form() {
|
||||||
|
return this.groupNameForm.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddClick() {
|
||||||
|
if (this.groupNameForm.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const groupName = this.groupNameForm.controls['groupName'].value;
|
||||||
|
const toAdd = Array.from(this.usersToAdd.values());
|
||||||
|
|
||||||
|
|
||||||
|
this.groupService.addGroup(this.controller, groupName)
|
||||||
|
.subscribe((group) => {
|
||||||
|
toAdd.forEach((user: User) => {
|
||||||
|
this.groupService.addMemberToGroup(this.controller, group, user)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.toasterService.success(`user ${user.username} was added`);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.toasterService.error(`An error occur while trying to add user ${user.username} to group ${groupName}`);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}, (error) => {
|
||||||
|
this.toasterService.error(`An error occur while trying to create new group ${groupName}`);
|
||||||
|
this.dialogRef.close(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onNoClick() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedUser(user: User) {
|
||||||
|
this.usersToAdd.add(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
delUser(user: User) {
|
||||||
|
this.usersToAdd.delete(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filter(value: string): User[] {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
|
||||||
|
return this.users.filter(option => option.username.toLowerCase().includes(filterValue)
|
||||||
|
|| (option.email?.toLowerCase().includes(filterValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayFn(value): string {
|
||||||
|
return value && value.username ? value.username : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { timer } from 'rxjs';
|
||||||
|
import { map, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { Controller } from "../../../models/controller";
|
||||||
|
import { GroupService } from "../../../services/group.service";
|
||||||
|
|
||||||
|
export const groupNameAsyncValidator = (controller: Controller, groupService: GroupService) => {
|
||||||
|
return (control: FormControl) => {
|
||||||
|
return timer(500).pipe(
|
||||||
|
switchMap(() => groupService.getGroups(controller)),
|
||||||
|
map((response) => {
|
||||||
|
console.log(response);
|
||||||
|
return (response.find((n) => n.name === control.value) ? { projectExist: true } : null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
<h1 mat-dialog-title>Are you sure to delete group named: </h1>
|
||||||
|
<p *ngFor="let group of data.groups">{{group.name}}</p>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<button mat-button (click)="onCancel()" color="accent">No, cancel</button>
|
||||||
|
<button mat-button (click)="onDelete()" tabindex="2" class="add-project-button" mat-raised-button color="primary">
|
||||||
|
Yes, delete!
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,6 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {Group} from "@models/groups/group";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-delete-group-dialog',
|
||||||
|
templateUrl: './delete-group-dialog.component.html',
|
||||||
|
styleUrls: ['./delete-group-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class DeleteGroupDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private dialogRef: MatDialogRef<DeleteGroupDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { groups: Group[] }) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete() {
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<div class="content" *ngIf="isReady; else loading">
|
||||||
|
<div class="default-header">
|
||||||
|
<div class="row">
|
||||||
|
<h1 class="col">Groups management</h1>
|
||||||
|
<button class="col" mat-raised-button color="primary" (click)="onDelete(selection.selected)" class="add-group-button" [disabled]="selection.selected.length == 0">
|
||||||
|
Delete selected groups
|
||||||
|
</button>
|
||||||
|
<button class="col" mat-raised-button color="primary" (click)="addGroup()" class="add-group-button">
|
||||||
|
Add group
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<mat-form-field class="full-width">
|
||||||
|
<input matInput placeholder="Search by name" [(ngModel)]="searchText" [ngModelOptions]="{ standalone: true }" />
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="default-content">
|
||||||
|
<table mat-table [dataSource]="dataSource | groupFilter: searchText" class="mat-elevation-z8" matSort #groupsSort="matSort">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="select" >
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="small-col">
|
||||||
|
<mat-checkbox (change)="$event ? masterToggle() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row" class="small-col">
|
||||||
|
<mat-checkbox (click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)">
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th>
|
||||||
|
<td mat-cell *matCellDef="let element"><a class="table-link" routerLink="/controller/{{controller.id}}/management/groups/{{element.user_group_id}}">{{element.name}}</a> </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="created_at">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Creation date</th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.created_at}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="updated_at">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Last update</th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.updated_at}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="is_builtin">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> is build in</th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.is_builtin}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
|
<td mat-cell *matCellDef="let element"><button mat-button (click)="onDelete([element])"><mat-icon>delete</mat-icon></button></td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<mat-paginator #groupsPaginator="matPaginator"
|
||||||
|
[pageSizeOptions]="[5, 10, 20]"
|
||||||
|
showFirstLastButtons
|
||||||
|
aria-label="Select page">
|
||||||
|
</mat-paginator>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-template #loading>
|
||||||
|
<div>
|
||||||
|
<mat-spinner class="loader"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
@ -0,0 +1,26 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 940px;
|
||||||
|
margin-left: -470px;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-group-button {
|
||||||
|
height: 40px;
|
||||||
|
width: 160px;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
margin: auto;
|
||||||
|
height: 175px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 175px;
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {ControllerService} from "../../services/controller.service";
|
||||||
|
import {ToasterService} from "../../services/toaster.service";
|
||||||
|
import {GroupService} from "../../services/group.service";
|
||||||
|
import {Controller} from "../../models/controller";
|
||||||
|
import {Group} from "../../models/groups/group";
|
||||||
|
import {MatSort, Sort} from "@angular/material/sort";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {AddGroupDialogComponent} from "@components/group-management/add-group-dialog/add-group-dialog.component";
|
||||||
|
import {DeleteGroupDialogComponent} from "@components/group-management/delete-group-dialog/delete-group-dialog.component";
|
||||||
|
import {SelectionModel} from "@angular/cdk/collections";
|
||||||
|
import {forkJoin} from "rxjs";
|
||||||
|
import {MatPaginator} from "@angular/material/paginator";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-group-management',
|
||||||
|
templateUrl: './group-management.component.html',
|
||||||
|
styleUrls: ['./group-management.component.scss']
|
||||||
|
})
|
||||||
|
export class GroupManagementComponent implements OnInit {
|
||||||
|
controller: Controller;
|
||||||
|
|
||||||
|
@ViewChildren('groupsPaginator') groupsPaginator: QueryList<MatPaginator>;
|
||||||
|
@ViewChildren('groupsSort') groupsSort: QueryList<MatSort>;
|
||||||
|
|
||||||
|
public displayedColumns = ['select', 'name', 'created_at', 'updated_at', 'is_builtin', 'delete'];
|
||||||
|
selection = new SelectionModel<Group>(true, []);
|
||||||
|
groups: Group[];
|
||||||
|
dataSource = new MatTableDataSource<Group>();
|
||||||
|
searchText: string;
|
||||||
|
isReady = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private controllerService: ControllerService,
|
||||||
|
private toasterService: ToasterService,
|
||||||
|
public groupService: GroupService,
|
||||||
|
public dialog: MatDialog
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const controllerId = this.route.parent.snapshot.paramMap.get('controller_id');
|
||||||
|
this.controllerService.get(+controllerId).then((controller: Controller) => {
|
||||||
|
this.controller = controller;
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.groupsPaginator.changes.subscribe((comps: QueryList <MatPaginator>) =>
|
||||||
|
{
|
||||||
|
this.dataSource.paginator = comps.first;
|
||||||
|
});
|
||||||
|
this.groupsSort.changes.subscribe((comps: QueryList<MatSort>) => {
|
||||||
|
this.dataSource.sort = comps.first;
|
||||||
|
});
|
||||||
|
this.dataSource.sortingDataAccessor = (item, property) => {
|
||||||
|
switch (property) {
|
||||||
|
case 'name':
|
||||||
|
return item[property] ? item[property].toLowerCase() : '';
|
||||||
|
default:
|
||||||
|
return item[property];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllSelected() {
|
||||||
|
const numSelected = this.selection.selected.length;
|
||||||
|
const numRows = this.groups.length;
|
||||||
|
return numSelected === numRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
masterToggle() {
|
||||||
|
this.isAllSelected() ?
|
||||||
|
this.selection.clear() :
|
||||||
|
this.groups.forEach(row => this.selection.select(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
addGroup() {
|
||||||
|
this.dialog
|
||||||
|
.open(AddGroupDialogComponent, {width: '600px', height: '500px', data: {controller: this.controller}})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((added: boolean) => {
|
||||||
|
if (added) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.groupService.getGroups(this.controller).subscribe((groups: Group[]) => {
|
||||||
|
this.isReady = true;
|
||||||
|
this.groups = groups;
|
||||||
|
this.dataSource.data = groups;
|
||||||
|
this.selection.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(groupsToDelete: Group[]) {
|
||||||
|
this.dialog
|
||||||
|
.open(DeleteGroupDialogComponent, {width: '500px', height: '250px', data: {groups: groupsToDelete}})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((isDeletedConfirm) => {
|
||||||
|
if (isDeletedConfirm) {
|
||||||
|
const observables = groupsToDelete.map((group: Group) => this.groupService.delete(this.controller, group.user_group_id));
|
||||||
|
forkJoin(observables)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.toasterService.error(`An error occur while trying to delete group`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { ImageManagerService } from 'app/services/image-manager.service';
|
import { ImageManagerService } from 'app/services/image-manager.service';
|
||||||
import { ServerService } from '../../../services/server.service';
|
import { ControllerService } from '../../../services/controller.service';
|
||||||
import { MockedServerService } from '../../../services/server.service.spec';
|
import { MockedControllerService } from '../../../services/controller.service.spec';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
|
|
||||||
import { AddImageDialogComponent } from './add-image-dialog.component';
|
import { AddImageDialogComponent } from './add-image-dialog.component';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
@ -17,7 +17,7 @@ import { MockedToasterService } from 'app/services/toaster.service.spec';
|
|||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
export class MockedImageManagerService {
|
export class MockedImageManagerService {
|
||||||
public getImages(server: Server) {
|
public getImages(controller:Controller ) {
|
||||||
return of();
|
return of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ export class MockedImageManagerService {
|
|||||||
describe('AddImageDialogComponent', () => {
|
describe('AddImageDialogComponent', () => {
|
||||||
let component: AddImageDialogComponent;
|
let component: AddImageDialogComponent;
|
||||||
let fixture: ComponentFixture<AddImageDialogComponent>;
|
let fixture: ComponentFixture<AddImageDialogComponent>;
|
||||||
|
|
||||||
let mockedServerService = new MockedServerService();
|
let mockedControllerService = new MockedControllerService();
|
||||||
let mockedImageManagerService = new MockedImageManagerService()
|
let mockedImageManagerService = new MockedImageManagerService()
|
||||||
let mockedToasterService = new MockedToasterService()
|
let mockedToasterService = new MockedToasterService()
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ describe('AddImageDialogComponent', () => {
|
|||||||
MatSnackBarModule
|
MatSnackBarModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ServerService, useValue: mockedServerService },
|
{ provide: ControllerService, useValue: mockedControllerService },
|
||||||
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
||||||
{ provide: MatDialogRef, useValue: {} },
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
@ -3,7 +3,7 @@ import { Component, Inject, OnInit } from '@angular/core';
|
|||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { UploadServiceService } from 'app/common/uploading-processbar/upload-service.service';
|
import { UploadServiceService } from 'app/common/uploading-processbar/upload-service.service';
|
||||||
import { FileItem, FileUploader, ParsedResponseHeaders } from 'ng2-file-upload';
|
import { FileItem, FileUploader, ParsedResponseHeaders } from 'ng2-file-upload';
|
||||||
import { Server } from '../../../models/server';
|
import{ Controller } from '../../../models/controller';
|
||||||
import { ImageManagerService } from '../../../services/image-manager.service';
|
import { ImageManagerService } from '../../../services/image-manager.service';
|
||||||
import { ToasterService } from '../../../services/toaster.service';
|
import { ToasterService } from '../../../services/toaster.service';
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ import { ToasterService } from '../../../services/toaster.service';
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AddImageDialogComponent implements OnInit {
|
export class AddImageDialogComponent implements OnInit {
|
||||||
server: Server;
|
controller:Controller ;
|
||||||
isInstallAppliance: boolean = false;
|
isInstallAppliance: boolean = false;
|
||||||
install_appliance: boolean = false;
|
install_appliance: boolean = false;
|
||||||
selectFile: any = [];
|
selectFile: any = [];
|
||||||
@ -36,7 +36,7 @@ export class AddImageDialogComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.server = this.data;
|
this.controller =this.data;
|
||||||
|
|
||||||
this.uploaderImage = new FileUploader({});
|
this.uploaderImage = new FileUploader({});
|
||||||
this.uploaderImage.onAfterAddingFile = (file) => {
|
this.uploaderImage.onAfterAddingFile = (file) => {
|
||||||
@ -98,11 +98,11 @@ export class AddImageDialogComponent implements OnInit {
|
|||||||
let file = event;
|
let file = event;
|
||||||
let fileReader: FileReader = new FileReader();
|
let fileReader: FileReader = new FileReader();
|
||||||
fileReader.onloadend = () => {
|
fileReader.onloadend = () => {
|
||||||
const url = this.imageService.getImagePath(this.server, this.install_appliance, fileName);
|
const url = this.imageService.getImagePath(this.controller, this.install_appliance, fileName);
|
||||||
const itemToUpload = this.uploaderImage.queue[i];
|
const itemToUpload = this.uploaderImage.queue[i];
|
||||||
itemToUpload.url = url;
|
itemToUpload.url = url;
|
||||||
if ((itemToUpload as any).options) (itemToUpload as any).options.disableMultipart = true;
|
if ((itemToUpload as any).options) (itemToUpload as any).options.disableMultipart = true;
|
||||||
(itemToUpload as any).options.headers = [{ name: 'Authorization', value: 'Bearer ' + this.server.authToken }];
|
(itemToUpload as any).options.headers = [{ name: 'Authorization', value: 'Bearer ' + this.controller.authToken }];
|
||||||
this.uploaderImage.uploadItem(itemToUpload);
|
this.uploaderImage.uploadItem(itemToUpload);
|
||||||
};
|
};
|
||||||
fileReader.readAsText(file);
|
fileReader.readAsText(file);
|
||||||
|
@ -10,14 +10,14 @@ import { MockedToasterService } from 'app/services/toaster.service.spec';
|
|||||||
import { Server } from 'http';
|
import { Server } from 'http';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ImageManagerService } from '../../../services/image-manager.service';
|
import { ImageManagerService } from '../../../services/image-manager.service';
|
||||||
import { ServerService } from '../../../services/server.service';
|
import { ControllerService } from '../../../services/controller.service';
|
||||||
import { MockedServerService } from '../../../services/server.service.spec';
|
import { MockedControllerService } from '../../../services/controller.service.spec';
|
||||||
import { ImageManagerComponent } from '../image-manager.component';
|
import { ImageManagerComponent } from '../image-manager.component';
|
||||||
|
|
||||||
import { DeleteAllImageFilesDialogComponent } from './deleteallfiles-dialog.component';
|
import { DeleteAllImageFilesDialogComponent } from './deleteallfiles-dialog.component';
|
||||||
|
|
||||||
export class MockedImageManagerService {
|
export class MockedImageManagerService {
|
||||||
public deleteALLFile(server: Server, image_path) {
|
public deleteALLFile(controller:Server , image_path) {
|
||||||
return of();
|
return of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ export class MockedImageManagerService {
|
|||||||
describe('DeleteAllImageFilesDialogComponent', () => {
|
describe('DeleteAllImageFilesDialogComponent', () => {
|
||||||
let component: DeleteAllImageFilesDialogComponent;
|
let component: DeleteAllImageFilesDialogComponent;
|
||||||
let fixture: ComponentFixture<DeleteAllImageFilesDialogComponent>;
|
let fixture: ComponentFixture<DeleteAllImageFilesDialogComponent>;
|
||||||
let mockedServerService = new MockedServerService();
|
let mockedControllerService = new MockedControllerService();
|
||||||
let mockedImageManagerService = new MockedImageManagerService()
|
let mockedImageManagerService = new MockedImageManagerService()
|
||||||
let mockedToasterService = new MockedToasterService()
|
let mockedToasterService = new MockedToasterService()
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ export class MockedImageManagerService {
|
|||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ServerService, useValue: mockedServerService },
|
{ provide: ControllerService, useValue: mockedControllerService },
|
||||||
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
||||||
{ provide: MatDialogRef, useValue: {} },
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
@ -35,7 +35,7 @@ export class DeleteAllImageFilesDialogComponent implements OnInit {
|
|||||||
deleteFile() {
|
deleteFile() {
|
||||||
const calls = [];
|
const calls = [];
|
||||||
this.deleteData.deleteFilesPaths.forEach(pathElement => {
|
this.deleteData.deleteFilesPaths.forEach(pathElement => {
|
||||||
calls.push(this.imageService.deleteFile(this.deleteData.server, pathElement.filename).pipe(catchError(error => of(error))))
|
calls.push(this.imageService.deleteFile(this.deleteData.controller, pathElement.filename).pipe(catchError(error => of(error))))
|
||||||
});
|
});
|
||||||
Observable.forkJoin(calls).subscribe(responses => {
|
Observable.forkJoin(calls).subscribe(responses => {
|
||||||
this.deleteFliesDetails = responses.filter(x => x !== null)
|
this.deleteFliesDetails = responses.filter(x => x !== null)
|
||||||
|
@ -20,14 +20,14 @@ export class imageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class imageDataSource extends DataSource<Image> {
|
export class imageDataSource extends DataSource<Image> {
|
||||||
constructor(private serverDatabase: imageDatabase) {
|
constructor(private controllerDatabase: imageDatabase) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(): Observable<Image[]> {
|
connect(): Observable<Image[]> {
|
||||||
return merge(this.serverDatabase.dataChange).pipe(
|
return merge(this.controllerDatabase.dataChange).pipe(
|
||||||
map(() => {
|
map(() => {
|
||||||
return this.serverDatabase.data;
|
return this.controllerDatabase.data;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="default-content">
|
<div class="default-content">
|
||||||
<app-server-discovery></app-server-discovery>
|
<app-controller-discovery></app-controller-discovery>
|
||||||
|
|
||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<mat-table #table [dataSource]="dataSource">
|
<mat-table #table [dataSource]="dataSource">
|
||||||
|
@ -6,10 +6,10 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { ImageManagerService } from 'app/services/image-manager.service';
|
import { ImageManagerService } from 'app/services/image-manager.service';
|
||||||
import { ServerService } from 'app/services/server.service';
|
import { ControllerService } from 'app/services/controller.service';
|
||||||
import { MockedServerService } from 'app/services/server.service.spec';
|
import { MockedControllerService } from 'app/services/controller.service.spec';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
|
|
||||||
import { ImageManagerComponent } from './image-manager.component';
|
import { ImageManagerComponent } from './image-manager.component';
|
||||||
import { Image } from '../../models/images';
|
import { Image } from '../../models/images';
|
||||||
@ -23,11 +23,11 @@ import { ToasterService } from 'app/services/toaster.service';
|
|||||||
import { MockedToasterService } from 'app/services/toaster.service.spec';
|
import { MockedToasterService } from 'app/services/toaster.service.spec';
|
||||||
|
|
||||||
export class MockedImageManagerService {
|
export class MockedImageManagerService {
|
||||||
public getImages(server: Server) {
|
public getImages(controller:Controller ) {
|
||||||
return of();
|
return of();
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteFile(server: Server, image_path) {
|
public deleteFile(controller:Controller , image_path) {
|
||||||
return of();
|
return of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ describe('ImageManagerComponent', () => {
|
|||||||
let component: ImageManagerComponent;
|
let component: ImageManagerComponent;
|
||||||
let fixture: ComponentFixture<ImageManagerComponent>;
|
let fixture: ComponentFixture<ImageManagerComponent>;
|
||||||
|
|
||||||
let mockedServerService = new MockedServerService();
|
let mockedControllerService = new MockedControllerService();
|
||||||
let mockedImageManagerService = new MockedImageManagerService()
|
let mockedImageManagerService = new MockedImageManagerService()
|
||||||
let mockedProgressService = new MockedProgressService()
|
let mockedProgressService = new MockedProgressService()
|
||||||
let mockedVersionService = new MockedVersionService()
|
let mockedVersionService = new MockedVersionService()
|
||||||
@ -59,7 +59,7 @@ describe('ImageManagerComponent', () => {
|
|||||||
provide: ActivatedRoute,
|
provide: ActivatedRoute,
|
||||||
useValue: activatedRoute,
|
useValue: activatedRoute,
|
||||||
},
|
},
|
||||||
{ provide: ServerService, useValue: mockedServerService },
|
{ provide: ControllerService, useValue: mockedControllerService },
|
||||||
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
{ provide: ImageManagerService, useValue: mockedImageManagerService },
|
||||||
{ provide: ProgressService, useValue: mockedProgressService },
|
{ provide: ProgressService, useValue: mockedProgressService },
|
||||||
{ provide: VersionService, useValue: mockedVersionService },
|
{ provide: VersionService, useValue: mockedVersionService },
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { ServerService } from '../../services/server.service';
|
import { ControllerService } from '../../services/controller.service';
|
||||||
import { VersionService } from '../../services/version.service';
|
import { VersionService } from '../../services/version.service';
|
||||||
import { ProgressService } from 'app/common/progress/progress.service';
|
import { ProgressService } from 'app/common/progress/progress.service';
|
||||||
import { Image } from '../../models/images';
|
import { Image } from '../../models/images';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
import { ImageManagerService } from "../../services/image-manager.service";
|
import { ImageManagerService } from "../../services/image-manager.service";
|
||||||
import { DataSource, SelectionModel } from '@angular/cdk/collections';
|
import { DataSource, SelectionModel } from '@angular/cdk/collections';
|
||||||
import { AddImageDialogComponent } from './add-image-dialog/add-image-dialog.component';
|
import { AddImageDialogComponent } from './add-image-dialog/add-image-dialog.component';
|
||||||
@ -19,7 +19,7 @@ import { imageDataSource, imageDatabase } from "./image-database-file";
|
|||||||
styleUrls: ['./image-manager.component.scss']
|
styleUrls: ['./image-manager.component.scss']
|
||||||
})
|
})
|
||||||
export class ImageManagerComponent implements OnInit {
|
export class ImageManagerComponent implements OnInit {
|
||||||
server: Server;
|
controller:Controller ;
|
||||||
public version: string;
|
public version: string;
|
||||||
dataSource: imageDataSource;
|
dataSource: imageDataSource;
|
||||||
imageDatabase = new imageDatabase();
|
imageDatabase = new imageDatabase();
|
||||||
@ -32,7 +32,7 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
private imageService: ImageManagerService,
|
private imageService: ImageManagerService,
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private serverService: ServerService,
|
private controllerService: ControllerService,
|
||||||
private versionService: VersionService,
|
private versionService: VersionService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private toasterService: ToasterService,
|
private toasterService: ToasterService,
|
||||||
@ -40,13 +40,13 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
let server_id = parseInt(this.route.snapshot.paramMap.get('server_id'));
|
let controller_id = parseInt(this.route.snapshot.paramMap.get('controller_id'));
|
||||||
this.serverService.get(server_id).then((server: Server) => {
|
this.controllerService.get(controller_id).then((controller:Controller ) => {
|
||||||
this.server = server;
|
this.controller = controller;
|
||||||
if (server.authToken) {
|
if (controller.authToken) {
|
||||||
this.getImages()
|
this.getImages()
|
||||||
}
|
}
|
||||||
// this.versionService.get(this.server).subscribe((version: Version) => {
|
// this.versionService.get(this.controller).subscribe((version: Version) => {
|
||||||
// this.version = version.version;
|
// this.version = version.version;
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
@ -54,7 +54,7 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getImages() {
|
getImages() {
|
||||||
this.imageService.getImages(this.server).subscribe(
|
this.imageService.getImages(this.controller).subscribe(
|
||||||
(images: Image[]) => {
|
(images: Image[]) => {
|
||||||
this.imageDatabase.addImages(images)
|
this.imageDatabase.addImages(images)
|
||||||
},
|
},
|
||||||
@ -66,7 +66,7 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteFile(path) {
|
deleteFile(path) {
|
||||||
this.imageService.deleteFile(this.server, path).subscribe(
|
this.imageService.deleteFile(this.controller, path).subscribe(
|
||||||
(res) => {
|
(res) => {
|
||||||
this.getImages()
|
this.getImages()
|
||||||
this.unChecked()
|
this.unChecked()
|
||||||
@ -106,7 +106,7 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
maxHeight: '550px',
|
maxHeight: '550px',
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
disableClose: true,
|
disableClose: true,
|
||||||
data: this.server
|
data: this.controller
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((isAddes: boolean) => {
|
dialogRef.afterClosed().subscribe((isAddes: boolean) => {
|
||||||
@ -128,7 +128,7 @@ export class ImageManagerComponent implements OnInit {
|
|||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
disableClose: true,
|
disableClose: true,
|
||||||
data: {
|
data: {
|
||||||
server: this.server,
|
controller: this.controller,
|
||||||
deleteFilesPaths: this.selection.selected
|
deleteFilesPaths: this.selection.selected
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,11 +2,11 @@ import { Component, DoCheck, OnInit, ViewEncapsulation } from '@angular/core';
|
|||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { AuthResponse } from '../../models/authResponse';
|
import { AuthResponse } from '../../models/authResponse';
|
||||||
import { Server } from '../../models/server';
|
import{ Controller } from '../../models/controller';
|
||||||
import { Version } from '../../models/version';
|
import { Version } from '../../models/version';
|
||||||
import { LoginService } from '../../services/login.service';
|
import { LoginService } from '../../services/login.service';
|
||||||
import { ServerDatabase } from '../../services/server.database';
|
import { ControllerDatabase } from '../../services/controller.database';
|
||||||
import { ServerService } from '../../services/server.service';
|
import { ControllerService } from '../../services/controller.service';
|
||||||
import { ThemeService } from '../../services/theme.service';
|
import { ThemeService } from '../../services/theme.service';
|
||||||
import { ToasterService } from '../../services/toaster.service';
|
import { ToasterService } from '../../services/toaster.service';
|
||||||
import { VersionService } from '../../services/version.service';
|
import { VersionService } from '../../services/version.service';
|
||||||
@ -18,7 +18,7 @@ import { VersionService } from '../../services/version.service';
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnInit, DoCheck {
|
export class LoginComponent implements OnInit, DoCheck {
|
||||||
private server: Server;
|
private controller:Controller ;
|
||||||
public version: string;
|
public version: string;
|
||||||
public isLightThemeEnabled: boolean = false;
|
public isLightThemeEnabled: boolean = false;
|
||||||
public loginError: boolean = false;
|
public loginError: boolean = false;
|
||||||
@ -33,8 +33,8 @@ export class LoginComponent implements OnInit, DoCheck {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private loginService: LoginService,
|
private loginService: LoginService,
|
||||||
private serverService: ServerService,
|
private controllerService: ControllerService,
|
||||||
private serverDatabase: ServerDatabase,
|
private controllerDatabase: ControllerDatabase,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private toasterService: ToasterService,
|
private toasterService: ToasterService,
|
||||||
@ -43,16 +43,16 @@ export class LoginComponent implements OnInit, DoCheck {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const server_id = this.route.snapshot.paramMap.get('server_id');
|
const controller_id = this.route.snapshot.paramMap.get('controller_id');
|
||||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||||
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
|
this.controllerService.get(parseInt(controller_id, 10)).then((controller:Controller ) => {
|
||||||
this.server = server;
|
this.controller = controller;
|
||||||
|
|
||||||
if (server.authToken) {
|
if (controller.authToken) {
|
||||||
this.router.navigate(['/server', this.server.id, 'projects']);
|
this.router.navigate(['/controller', this.controller.id, 'projects']);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.versionService.get(this.server).subscribe((version: Version) => {
|
this.versionService.get(this.controller).subscribe((version: Version) => {
|
||||||
this.version = version.version;
|
this.version = version.version;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -78,17 +78,17 @@ export class LoginComponent implements OnInit, DoCheck {
|
|||||||
let username = this.loginForm.get('username').value;
|
let username = this.loginForm.get('username').value;
|
||||||
let password = this.loginForm.get('password').value;
|
let password = this.loginForm.get('password').value;
|
||||||
|
|
||||||
this.loginService.login(this.server, username, password).subscribe(
|
this.loginService.login(this.controller, username, password).subscribe(
|
||||||
async (response: AuthResponse) => {
|
async (response: AuthResponse) => {
|
||||||
let server = this.server;
|
let controller = this.controller;
|
||||||
server.authToken = response.access_token;
|
controller.authToken = response.access_token;
|
||||||
server.username = username;
|
controller.username = username;
|
||||||
server.password = password;
|
controller.password = password;
|
||||||
server.tokenExpired = false;
|
controller.tokenExpired = false;
|
||||||
await this.serverService.update(server);
|
await this.controllerService.update(controller);
|
||||||
|
|
||||||
if (this.returnUrl.length <= 1) {
|
if (this.returnUrl.length <= 1) {
|
||||||
this.router.navigate(['/server', this.server.id, 'projects']);
|
this.router.navigate(['/controller', this.controller.id, 'projects']);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigateByUrl(this.returnUrl);
|
this.router.navigateByUrl(this.returnUrl);
|
||||||
}
|
}
|
||||||
|
9
src/app/components/management/management.component.html
Normal file
9
src/app/components/management/management.component.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<nav mat-tab-nav-bar>
|
||||||
|
<a mat-tab-link *ngFor="let link of links"
|
||||||
|
(click)="activeLink = link"
|
||||||
|
routerLink="./{{link}}"
|
||||||
|
routerLinkActive
|
||||||
|
[active]="rla.isActive"
|
||||||
|
#rla="routerLinkActive">{{link.charAt(0).toUpperCase() + link.slice(1)}} </a>
|
||||||
|
</nav>
|
||||||
|
<router-outlet></router-outlet>
|
40
src/app/components/management/management.component.ts
Normal file
40
src/app/components/management/management.component.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {Controller} from "@models/controller";
|
||||||
|
import {ControllerService} from "@services/controller.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-management',
|
||||||
|
templateUrl: './management.component.html',
|
||||||
|
styleUrls: ['./management.component.scss']
|
||||||
|
})
|
||||||
|
export class ManagementComponent implements OnInit {
|
||||||
|
|
||||||
|
controller: Controller;
|
||||||
|
links = ['users', 'groups', 'roles', 'permissions'];
|
||||||
|
activeLink: string = this.links[0];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
public router: Router,
|
||||||
|
private controllerService: ControllerService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const controllerId = this.route.snapshot.paramMap.get('controller_id');
|
||||||
|
this.controllerService.get(+controllerId).then((controller: Controller) => {
|
||||||
|
this.controller = controller;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,6 @@
|
|||||||
<div class="default-header">
|
<div class="default-header">
|
||||||
<br />
|
<br />
|
||||||
<h1>404 Page not found</h1>
|
<h1>404 Page not found</h1>
|
||||||
<button mat-button routerLink="/servers">Navigate to homepage</button>
|
<button mat-button routerLink="/controllers">Navigate to homepage</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
<button [ngClass]="{allow: action === 'ALLOW', deny: action === 'DENY'}"
|
||||||
|
mat-button
|
||||||
|
[disabled]="disabled"
|
||||||
|
(click)="change()">
|
||||||
|
{{action}}
|
||||||
|
</button>
|
@ -0,0 +1,8 @@
|
|||||||
|
.allow {
|
||||||
|
background-color: green;
|
||||||
|
border-radius: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deny {
|
||||||
|
background-color: darkred;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Software Name : GNS3 Web UI
|
||||||
|
* Version: 3
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This software is distributed under the GPL-3.0 or any later version,
|
||||||
|
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
* or see the "LICENSE" file for more details.
|
||||||
|
*
|
||||||
|
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||||
|
*/
|
||||||
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
|
import {PermissionActions} from "@models/api/permission";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-action-button',
|
||||||
|
templateUrl: './action-button.component.html',
|
||||||
|
styleUrls: ['./action-button.component.scss']
|
||||||
|
})
|
||||||
|
export class ActionButtonComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly DENY = 'DENY';
|
||||||
|
readonly ALLOW = 'ALLOW';
|
||||||
|
@Input() action: PermissionActions;
|
||||||
|
@Input() disabled = true;
|
||||||
|
@Output() update = new EventEmitter<PermissionActions>();
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
change() {
|
||||||
|
this.action === PermissionActions.DENY ? this.action = PermissionActions.ALLOW : this.action = PermissionActions.DENY;
|
||||||
|
this.update.emit(this.action);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
<div class="box-border">
|
||||||
|
<div *ngIf="edit; else add">
|
||||||
|
<div class="edit-mode">
|
||||||
|
<div class="information-box">
|
||||||
|
<div>
|
||||||
|
<app-path-auto-complete
|
||||||
|
[controller]="controller"
|
||||||
|
(update)="permission.path = $event"></app-path-auto-complete>
|
||||||
|
</div>
|
||||||
|
<div class="methods">
|
||||||
|
<app-action-button
|
||||||
|
[disabled]="false"
|
||||||
|
[action]="permission.action"></app-action-button>
|
||||||
|
<div *ngFor="let method of apiInformation.getMethods(permission.path) | async">
|
||||||
|
<app-method-button
|
||||||
|
[name]="method"
|
||||||
|
[disabled]="false"
|
||||||
|
(update)="updateMethod($event)"></app-method-button>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<mat-form-field>
|
||||||
|
<input
|
||||||
|
[(ngModel)]="permission.description"
|
||||||
|
matInput
|
||||||
|
type="text"
|
||||||
|
placeholder="Description"/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="button-box">
|
||||||
|
<button mat-button (click)="reset()">
|
||||||
|
<mat-icon>cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="save()">
|
||||||
|
<mat-icon>done</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-template #add>
|
||||||
|
<div class="not-edit">
|
||||||
|
<button mat-button (click)="edit = true">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user