mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-29 23:54:11 +00:00
Communication betwen angular and local-server.js
This commit is contained in:
parent
b00604cc39
commit
af90fe46d0
117
local-server.js
117
local-server.js
@ -38,11 +38,13 @@ exports.getLocalServerPath = async () => {
|
||||
}
|
||||
|
||||
exports.startLocalServer = async (server) => {
|
||||
return await run(server);
|
||||
return await run(server, {
|
||||
logStdout: true
|
||||
});
|
||||
}
|
||||
|
||||
exports.stopLocalServer = async (server) => {
|
||||
return await stop(server);
|
||||
return await stop(server.name);
|
||||
}
|
||||
|
||||
function getServerArguments(server, overrides) {
|
||||
@ -54,56 +56,99 @@ function getChannelForServer(server) {
|
||||
return `local-server-run-${server.name}`;
|
||||
}
|
||||
|
||||
function notifyStatus(status) {
|
||||
ipcMain.emit('local-server-status-events', status);
|
||||
}
|
||||
|
||||
async function stopAll() {
|
||||
for(var serverName in runningServers) {
|
||||
let result, error = await stop(serverName);
|
||||
}
|
||||
console.log(`Stopped all servers`);
|
||||
for(var serverName in runningServers) {
|
||||
let result, error = await stop(serverName);
|
||||
}
|
||||
console.log(`Stopped all servers`);
|
||||
}
|
||||
|
||||
async function stop(serverName) {
|
||||
const runningServer = runningServers[serverName];
|
||||
const pid = runningServer.process.pid;
|
||||
console.log(`Stopping '${serverName}' with PID='${pid}'`);
|
||||
let pid = undefined;
|
||||
|
||||
const stopped = new Promise((resolve, reject) => {
|
||||
kill(pid, (error) => {
|
||||
if(error) {
|
||||
console.error(`Error occured during stopping '${serverName}' with PID='${pid}'`);
|
||||
reject(error);
|
||||
}
|
||||
else {
|
||||
console.log(`Stopped '${serverName}' with PID='${pid}'`);
|
||||
resolve(`Stopped '${serverName}' with PID='${pid}'`);
|
||||
}
|
||||
});
|
||||
const runningServer = runningServers[serverName];
|
||||
|
||||
if(runningServer !== undefined && runningServer.process) {
|
||||
pid = runningServer.process.pid;
|
||||
}
|
||||
|
||||
console.log(`Stopping '${serverName}' with PID='${pid}'`);
|
||||
|
||||
const stopped = new Promise((resolve, reject) => {
|
||||
if(pid === undefined) {
|
||||
resolve(`Server '${serverName} is already stopped`);
|
||||
return;
|
||||
}
|
||||
|
||||
kill(pid, (error) => {
|
||||
if(error) {
|
||||
console.error(`Error occured during stopping '${serverName}' with PID='${pid}'`);
|
||||
reject(error);
|
||||
}
|
||||
else {
|
||||
console.log(`Stopped '${serverName}' with PID='${pid}'`);
|
||||
resolve(`Stopped '${serverName}' with PID='${pid}'`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return stopped;
|
||||
return stopped;
|
||||
}
|
||||
|
||||
async function run(server, options) {
|
||||
if(!options) {
|
||||
options = {};
|
||||
if(!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const logStdout = options.logStdout || false;
|
||||
const logSterr = options.logSterr || false;
|
||||
|
||||
console.log(`Running '${server.path}'`);
|
||||
|
||||
let serverProcess = spawn(server.path, getServerArguments(server));
|
||||
|
||||
notifyStatus({
|
||||
serverName: server.name,
|
||||
status: 'started',
|
||||
message: `Server '${server.name}' started'`
|
||||
});
|
||||
|
||||
runningServers[server.name] = {
|
||||
process: serverProcess
|
||||
};
|
||||
|
||||
serverProcess.stdout.on('data', function(data) {
|
||||
if(logStdout) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const logStdout = options.logStdout || false;
|
||||
serverProcess.stderr.on('data', function(data) {
|
||||
if(logSterr) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Running '${server.path}'`);
|
||||
|
||||
let serverProcess = spawn(server.path, getServerArguments(server));
|
||||
|
||||
runningServers[server.name] = {
|
||||
process: serverProcess
|
||||
};
|
||||
|
||||
serverProcess.stdout.on('data', function(data) {
|
||||
if(logStdout) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
serverProcess.on('exit', (code, signal) => {
|
||||
notifyStatus({
|
||||
serverName: server.name,
|
||||
status: 'errored',
|
||||
message: `Server '${server.name}' has exited with status='${code}'`
|
||||
});
|
||||
});
|
||||
|
||||
serverProcess.on('error', (err) => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function main() {
|
||||
await run({
|
||||
name: 'my-local',
|
||||
|
5
main.js
5
main.js
@ -65,6 +65,11 @@ function createWindow () {
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
});
|
||||
|
||||
// forward event to renderer
|
||||
electron.ipcMain.on('local-server-status-events', (event) => {
|
||||
mainWindow.webContents.send('local-server-status-events', event);
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
|
@ -95,6 +95,7 @@ import { TextEditorDialogComponent } from './components/project-map/drawings-edi
|
||||
import { InstalledSoftwareService } from './services/installed-software.service';
|
||||
import { ExternalSoftwareDefinitionService } from './services/external-software-definition.service';
|
||||
import { PlatformService } from './services/platform.service';
|
||||
import { ServerManagementService } from './services/server-management.service';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -197,7 +198,8 @@ if (environment.production) {
|
||||
ToolsService,
|
||||
InstalledSoftwareService,
|
||||
ExternalSoftwareDefinitionService,
|
||||
PlatformService
|
||||
PlatformService,
|
||||
ServerManagementService
|
||||
],
|
||||
entryComponents: [
|
||||
AddServerDialogComponent,
|
||||
|
@ -1,29 +1,32 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Component, Inject, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
|
||||
import { Observable, merge } from 'rxjs';
|
||||
import { Observable, merge, Subscription } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { Server } from '../../models/server';
|
||||
import { ServerService } from '../../services/server.service';
|
||||
import { ServerDatabase } from '../../services/server.database';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { ServerManagementService } from '../../services/server-management.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-server-list',
|
||||
templateUrl: './servers.component.html',
|
||||
styleUrls: ['./servers.component.css']
|
||||
})
|
||||
export class ServersComponent implements OnInit {
|
||||
export class ServersComponent implements OnInit, OnDestroy {
|
||||
dataSource: ServerDataSource;
|
||||
displayedColumns = ['id', 'name', 'location', 'ip', 'port', 'actions'];
|
||||
serverStatusSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private serverService: ServerService,
|
||||
private serverDatabase: ServerDatabase,
|
||||
private electronService: ElectronService,
|
||||
private serverManagement: ServerManagementService,
|
||||
private changeDetector: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -32,6 +35,28 @@ export class ServersComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.dataSource = new ServerDataSource(this.serverDatabase);
|
||||
|
||||
this.serverStatusSubscription = this.serverManagement.serverStatusChanged.subscribe((serverStatus) => {
|
||||
const server = this.serverDatabase.find(serverStatus.serverName);
|
||||
if(!server) {
|
||||
return;
|
||||
}
|
||||
if(serverStatus.status === 'stopped') {
|
||||
server.status = 'stopped';
|
||||
}
|
||||
if(serverStatus.status === 'errored') {
|
||||
server.status = 'stopped';
|
||||
}
|
||||
if(serverStatus.status === 'started') {
|
||||
server.status = 'running';
|
||||
}
|
||||
this.serverDatabase.update(server);
|
||||
this.changeDetector.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.serverStatusSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
createModal() {
|
||||
@ -64,15 +89,11 @@ export class ServersComponent implements OnInit {
|
||||
}
|
||||
|
||||
async startServer(server: Server) {
|
||||
await this.electronService.remote.require('./local-server.js').startLocalServer(server);
|
||||
server.status = 'running';
|
||||
this.serverDatabase.update(server);
|
||||
await this.serverManagement.start(server);
|
||||
}
|
||||
|
||||
async stopServer(server: Server) {
|
||||
await this.electronService.remote.require('./local-server.js').stopLocalServer(server);
|
||||
server.status = 'stopped';
|
||||
this.serverDatabase.update(server);
|
||||
await this.serverManagement.stop(server);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,10 @@ import { MatIconModule, MatMenuModule, MatToolbarModule, MatProgressSpinnerModul
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ProgressComponent } from '../../common/progress/progress.component';
|
||||
import { ProgressService } from '../../common/progress/progress.service';
|
||||
import { ServerManagementService, ServerStateEvent } from '../../services/server-management.service';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
import { MockedToasterService } from '../../services/toaster.service.spec';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
|
||||
class ElectronServiceMock {
|
||||
@ -16,9 +20,13 @@ describe('DefaultLayoutComponent', () => {
|
||||
let component: DefaultLayoutComponent;
|
||||
let fixture: ComponentFixture<DefaultLayoutComponent>;
|
||||
let electronServiceMock: ElectronServiceMock;
|
||||
let serverManagementService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
electronServiceMock = new ElectronServiceMock();
|
||||
serverManagementService = {
|
||||
serverStatusChanged: new Subject<ServerStateEvent>()
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DefaultLayoutComponent, ProgressComponent],
|
||||
@ -28,6 +36,14 @@ describe('DefaultLayoutComponent', () => {
|
||||
provide: ElectronService,
|
||||
useValue: electronServiceMock
|
||||
},
|
||||
{
|
||||
provide: ServerManagementService,
|
||||
useValue: serverManagementService
|
||||
},
|
||||
{
|
||||
provide: ToasterService,
|
||||
useClass: MockedToasterService
|
||||
},
|
||||
ProgressService
|
||||
]
|
||||
}).compileComponents();
|
||||
@ -54,4 +70,23 @@ describe('DefaultLayoutComponent', () => {
|
||||
component.ngOnInit();
|
||||
expect(component.isInstalledSoftwareAvailable).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show error when server management service throw event', () => {
|
||||
const toaster: MockedToasterService = TestBed.get(ToasterService);
|
||||
serverManagementService.serverStatusChanged.next({
|
||||
status: 'errored',
|
||||
message: 'Message'
|
||||
});
|
||||
expect(toaster.errors).toEqual(['Message']);
|
||||
});
|
||||
|
||||
it('should not show error when server management service throw event', () => {
|
||||
component.ngOnDestroy();
|
||||
const toaster: MockedToasterService = TestBed.get(ToasterService);
|
||||
serverManagementService.serverStatusChanged.next({
|
||||
status: 'errored',
|
||||
message: 'Message'
|
||||
});
|
||||
expect(toaster.errors).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
|
||||
import { ServerManagementService } from '../../services/server-management.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-default-layout',
|
||||
@ -7,15 +10,30 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
templateUrl: './default-layout.component.html',
|
||||
styleUrls: ['./default-layout.component.css']
|
||||
})
|
||||
export class DefaultLayoutComponent implements OnInit {
|
||||
export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||
public isInstalledSoftwareAvailable = false;
|
||||
|
||||
serverStatusSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private electronService: ElectronService
|
||||
private electronService: ElectronService,
|
||||
private serverManagement: ServerManagementService,
|
||||
private toasterService: ToasterService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.isInstalledSoftwareAvailable = this.electronService.isElectronApp;
|
||||
|
||||
this.serverStatusSubscription = this.serverManagement.serverStatusChanged.subscribe((serverStatus) => {
|
||||
if(serverStatus.status === 'errored') {
|
||||
this.toasterService.error(serverStatus.message);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.serverStatusSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
93
src/app/services/server-management.service.spec.ts
Normal file
93
src/app/services/server-management.service.spec.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ServerManagementService } from './server-management.service';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { Server } from '../models/server';
|
||||
|
||||
describe('ServerManagementService', () => {
|
||||
let electronService;
|
||||
let callbacks
|
||||
let removed;
|
||||
let server;
|
||||
|
||||
beforeEach(() => {
|
||||
callbacks = [];
|
||||
removed = [];
|
||||
server = undefined;
|
||||
electronService = {
|
||||
isElectronApp: true,
|
||||
ipcRenderer: {
|
||||
on: (channel, callback) => {
|
||||
callbacks.push({
|
||||
channel: channel,
|
||||
callback: callback
|
||||
});
|
||||
},
|
||||
removeAllListeners: (name) => {
|
||||
removed.push(name);
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
require: (file) => {
|
||||
return {
|
||||
startLocalServer: (serv) => {
|
||||
server = serv;
|
||||
},
|
||||
stopLocalServer: (serv) => {
|
||||
server = serv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: ElectronService, useValue: electronService},
|
||||
ServerManagementService
|
||||
]
|
||||
}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ServerManagementService = TestBed.get(ServerManagementService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should attach when running as electron app', () => {
|
||||
TestBed.get(ServerManagementService);
|
||||
expect(callbacks.length).toEqual(1);
|
||||
expect(callbacks[0].channel).toEqual('local-server-status-events');
|
||||
});
|
||||
|
||||
it('should not attach when running as not electron app', () => {
|
||||
electronService.isElectronApp = false;
|
||||
TestBed.get(ServerManagementService);
|
||||
expect(callbacks.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should deattach when running as electron app', () => {
|
||||
const service: ServerManagementService = TestBed.get(ServerManagementService);
|
||||
service.ngOnDestroy();
|
||||
expect(removed).toEqual(['local-server-status-events']);
|
||||
});
|
||||
|
||||
it('should not deattach when running as not electron app', () => {
|
||||
electronService.isElectronApp = false;
|
||||
const service: ServerManagementService = TestBed.get(ServerManagementService);
|
||||
service.ngOnDestroy();
|
||||
expect(removed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should start local server', async () => {
|
||||
const service: ServerManagementService = TestBed.get(ServerManagementService);
|
||||
await service.start({ name: 'test'} as Server);
|
||||
expect(server).toEqual({ name: 'test'});
|
||||
});
|
||||
|
||||
it('should stop local server', async () => {
|
||||
const service: ServerManagementService = TestBed.get(ServerManagementService);
|
||||
await service.stop({ name: 'test2'} as Server);
|
||||
expect(server).toEqual({ name: 'test2'});
|
||||
});
|
||||
});
|
44
src/app/services/server-management.service.ts
Normal file
44
src/app/services/server-management.service.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { Server } from '../models/server';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export interface ServerStateEvent {
|
||||
serverName: string;
|
||||
status: "started" | "errored" | "stopped";
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ServerManagementService implements OnDestroy {
|
||||
|
||||
serverStatusChanged = new Subject<ServerStateEvent>();
|
||||
|
||||
constructor(
|
||||
private electronService: ElectronService
|
||||
) {
|
||||
if(this.electronService.isElectronApp) {
|
||||
this.electronService.ipcRenderer.on(this.statusChannel, (event, data) => {
|
||||
this.serverStatusChanged.next(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get statusChannel() {
|
||||
return 'local-server-status-events';
|
||||
}
|
||||
|
||||
async start(server: Server) {
|
||||
await this.electronService.remote.require('./local-server.js').startLocalServer(server);
|
||||
}
|
||||
|
||||
async stop(server: Server) {
|
||||
await this.electronService.remote.require('./local-server.js').stopLocalServer(server);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if(this.electronService.isElectronApp) {
|
||||
this.electronService.ipcRenderer.removeAllListeners(this.statusChannel);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,16 @@ export class ServerDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public find(serverName: string) {
|
||||
return this.data.find((server) => server.name === serverName);
|
||||
}
|
||||
|
||||
public findIndex(serverName: string) {
|
||||
return this.data.findIndex((server) => server.name === serverName);
|
||||
}
|
||||
|
||||
public update(server: Server) {
|
||||
const index = this.data.indexOf(server);
|
||||
const index = this.findIndex(server.name);
|
||||
if (index >= 0) {
|
||||
this.data[index] = server;
|
||||
this.dataChange.next(this.data.slice());
|
||||
|
Loading…
x
Reference in New Issue
Block a user