Brand new Angular client
This commit is contained in:
23
client/src/app/_services/language.service.ts
Normal file
23
client/src/app/_services/language.service.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LanguageService {
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private translateService: TranslateService
|
||||
) { }
|
||||
|
||||
public setLanguage(language: string): void {
|
||||
this.translateService.use(language);
|
||||
this.storageService.setLanguage(language);
|
||||
}
|
||||
|
||||
public getLanguage(): string {
|
||||
return this.storageService.getLanguage();
|
||||
}
|
||||
}
|
245
client/src/app/_services/storage.service.ts
Normal file
245
client/src/app/_services/storage.service.ts
Normal file
@ -0,0 +1,245 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ServerDto } from '../_dto/ServerDto';
|
||||
import { StoredDataDto } from '../_dto/StoredDataDto';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class StorageService {
|
||||
|
||||
//BehaviorSubjects for latest settings values
|
||||
public widerViewportSubject = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor() {
|
||||
this.initializeLocalStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize settings
|
||||
*/
|
||||
private initializeLocalStorage(): void {
|
||||
//If undefined, initialize localStorage
|
||||
if (typeof window.localStorage["WebConsole"] === 'undefined') {
|
||||
//Create empty object
|
||||
var storageObj: StoredDataDto = {
|
||||
servers: [],
|
||||
language: "",
|
||||
settings: {
|
||||
dateTimePrefix: true,
|
||||
retrieveLogFile: true,
|
||||
blurryUri: false,
|
||||
widerViewport: false
|
||||
}
|
||||
};
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage["WebConsole"] = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
//Initialize BehaviorSubjects
|
||||
this.widerViewportSubject.next(this.getSetting(SettingsEnum.WiderViewport));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all saved servers
|
||||
* @returns List of all servers saved
|
||||
*/
|
||||
public getAllServers(): ServerDto[] {
|
||||
var storageObj = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
return storageObj.servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server info given its name
|
||||
* @param serverName Server name
|
||||
* @returns Server details, null if not found
|
||||
*/
|
||||
public getServer(serverName: string): ServerDto | undefined {
|
||||
return this.getAllServers().find(e => e.serverName == serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new server or, in case a server with same name already exists, update it.
|
||||
* @param serverName Name of the server to add or update
|
||||
* @param serverUri URI of the server
|
||||
*/
|
||||
public saveServer(serverName: string, serverUri: string, serverPassword?: string): void {
|
||||
//Get all saved servers
|
||||
let servers = this.getAllServers();
|
||||
|
||||
let server = servers.find(e => e.serverName == serverName);
|
||||
if (server) {
|
||||
//If server exists, update it
|
||||
(server as ServerDto).serverURI = serverUri;
|
||||
if (serverPassword)
|
||||
(server as ServerDto).serverPassword = serverPassword;
|
||||
else if (serverPassword == null)
|
||||
(server as ServerDto).serverPassword = undefined;
|
||||
} else {
|
||||
//If it does not exist, add to the array
|
||||
const serverToSave: ServerDto = {
|
||||
serverName: serverName,
|
||||
serverURI: serverUri,
|
||||
serverPassword: serverPassword
|
||||
}
|
||||
|
||||
servers.push(serverToSave);
|
||||
}
|
||||
|
||||
//Overwrite array
|
||||
this.replaceAllServers(servers);
|
||||
}
|
||||
|
||||
moveServerToIndex(serverName: string, newIndex: number): void {
|
||||
//Prevent moving if index is not valid
|
||||
const listOfServers: ServerDto[] = this.getAllServers();
|
||||
if (newIndex < 0 || newIndex >= listOfServers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Prevent moving if server does not exist
|
||||
const serverIndex = listOfServers.findIndex(e => e.serverName == serverName);
|
||||
if (serverIndex === -1)
|
||||
return;
|
||||
|
||||
//Move server
|
||||
const serverToMove: ServerDto = listOfServers.find(e => e.serverName == serverName) as ServerDto;
|
||||
listOfServers.splice(serverIndex, 1); //Remove element from its current position
|
||||
listOfServers.splice(newIndex, 0, serverToMove); //Inject element in new position
|
||||
this.replaceAllServers(listOfServers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a server given its name
|
||||
* @param serverName Name of the server
|
||||
*/
|
||||
public deleteServer(serverName: string): void {
|
||||
//Get all servers
|
||||
var servers = this.getAllServers();
|
||||
|
||||
//Delete it
|
||||
servers = servers.filter(e => e.serverName != serverName)
|
||||
|
||||
//Save to LocalStorage
|
||||
this.replaceAllServers(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save to persistence a new default language
|
||||
* @param lang Language to set
|
||||
*/
|
||||
public setLanguage(lang: string): void {
|
||||
//Retrieve saved data
|
||||
var storageObj = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
storageObj.language = lang;
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage["WebConsole"] = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get saved language
|
||||
* @returns Language saved
|
||||
*/
|
||||
public getLanguage(): string {
|
||||
var storageObj = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
if (!storageObj.language)
|
||||
return "en_US";
|
||||
return storageObj.language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace ALL servers with the provided server list
|
||||
* @param newServerList New server list
|
||||
*/
|
||||
private replaceAllServers(newServerList: ServerDto[]) {
|
||||
//Retrieve saved data
|
||||
let storageObj = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
storageObj.servers = newServerList;
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage["WebConsole"] = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update setting value
|
||||
* @param setting Setting to set
|
||||
* @param value Value to set
|
||||
*/
|
||||
public setSetting(setting: SettingsEnum, value: any) {
|
||||
let currentSettings = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
|
||||
switch (setting) {
|
||||
case SettingsEnum.DateTimePrefix:
|
||||
currentSettings.settings.dateTimePrefix = value;
|
||||
break;
|
||||
case SettingsEnum.RetrieveLogFile:
|
||||
currentSettings.settings.retrieveLogFile = value;
|
||||
break;
|
||||
case SettingsEnum.BlurryUri:
|
||||
currentSettings.settings.blurryUri = value;
|
||||
break;
|
||||
case SettingsEnum.WiderViewport:
|
||||
currentSettings.settings.widerViewport = value;
|
||||
this.widerViewportSubject.next(value);
|
||||
break;
|
||||
}
|
||||
|
||||
//Save all
|
||||
let storageObj = JSON.parse(window.localStorage["WebConsole"]);
|
||||
storageObj.settings = currentSettings.settings;
|
||||
window.localStorage["WebConsole"] = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a setting
|
||||
* @param setting Setting to get
|
||||
* @returns Settings value
|
||||
*/
|
||||
public getSetting(setting: SettingsEnum) {
|
||||
let currentSettings = JSON.parse(window.localStorage["WebConsole"]) as StoredDataDto;
|
||||
|
||||
switch (setting) {
|
||||
case SettingsEnum.DateTimePrefix:
|
||||
return currentSettings.settings.dateTimePrefix;
|
||||
case SettingsEnum.RetrieveLogFile:
|
||||
return currentSettings.settings.retrieveLogFile;
|
||||
case SettingsEnum.BlurryUri:
|
||||
return currentSettings.settings.blurryUri;
|
||||
case SettingsEnum.WiderViewport:
|
||||
return currentSettings.settings.widerViewport;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from a Base64-encoded JSON
|
||||
* @param base64settings Encoded settings
|
||||
* @returns True if imported successfully, false otherwise
|
||||
*/
|
||||
public importSettings(base64settings: string): boolean {
|
||||
try {
|
||||
const decodedJsonSettings = atob(base64settings);
|
||||
window.localStorage["WebConsole"] = decodedJsonSettings;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Export settings
|
||||
* @returns A Base64-encoded JSON containing settings
|
||||
*/
|
||||
public getExportString(): string {
|
||||
return btoa(window.localStorage["WebConsole"]);
|
||||
}
|
||||
}
|
||||
|
||||
export enum SettingsEnum {
|
||||
DateTimePrefix,
|
||||
RetrieveLogFile,
|
||||
BlurryUri,
|
||||
WiderViewport
|
||||
}
|
204
client/src/app/_services/webconsole.service.ts
Normal file
204
client/src/app/_services/webconsole.service.ts
Normal file
@ -0,0 +1,204 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map, Observable, Observer, Subject } from 'rxjs';
|
||||
import { AnonymousSubject } from 'rxjs/internal/Subject';
|
||||
import { ActiveConnectionDto } from '../_dto/ActiveConnectionDto';
|
||||
import { WebSocketCommand } from '../_dto/command/WebSocketCommand';
|
||||
import { WebSocketCommandEnum } from '../_dto/command/WebSocketCommandEnum';
|
||||
import { ConnectionStatusEnum } from '../_dto/ConnectionStatusEnum';
|
||||
import { ConsoleOutputResponse } from '../_dto/response/ConsoleOutputResponse';
|
||||
import { CpuResponse } from '../_dto/response/CpuResponse';
|
||||
import { LoggedInResponse } from '../_dto/response/LoggedInResponse';
|
||||
import { LoginRequiredResponse } from '../_dto/response/LoginRequiredResponse';
|
||||
import { PlayersResponse } from '../_dto/response/PlayersResponse';
|
||||
import { RamResponse } from '../_dto/response/RamResponse';
|
||||
import { TpsResponse } from '../_dto/response/TpsResponse';
|
||||
import { UnknownCommandResponse } from '../_dto/response/UnknownCommandResponse';
|
||||
import { WebSocketResponse } from '../_dto/response/WebSocketResponse';
|
||||
import { ServerDto } from '../_dto/ServerDto';
|
||||
import { SettingsEnum, StorageService } from './storage.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WebconsoleService {
|
||||
|
||||
//Array as Index Signature which stores ActiveConnectionDto. This object
|
||||
private activeConnections: ActiveConnectionDto[] = [];
|
||||
//WebSocket connections stored separately, as we want all interactions with WebSockets to be done from this service.
|
||||
private webSocketClients: { [key: string]: WebSocket | undefined } = {};
|
||||
//Subject used to notify subscribers when a server is connected or disconnected
|
||||
private activeConnectionsChangedSubject$: Subject<void> = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Returns a list containing the server names WebConsole is currently connected to
|
||||
* @returns List of Server names
|
||||
*/
|
||||
public getCurrentConnectedServers(): string[] {
|
||||
return this.activeConnections.map(e => e.serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies subscribers when a server is connected or disconnected
|
||||
* @returns Subject used to notify when a server is connected or disconnected
|
||||
*/
|
||||
public getActiveConnectionsChangedSubject(): Subject<void> {
|
||||
return this.activeConnectionsChangedSubject$;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a server or returns previously created one
|
||||
* @param serverName Name of the server to connect to
|
||||
* @returns Created connection or previously created one, if not closed.
|
||||
*/
|
||||
public connect(serverName: string): ActiveConnectionDto {
|
||||
//If already connected to this server, return the already created Subject
|
||||
const activeConnection = this.activeConnections.find(e => e.serverName == serverName);
|
||||
if (activeConnection) {
|
||||
return activeConnection;
|
||||
}
|
||||
|
||||
//If not already connected, connect to server
|
||||
const server: ServerDto | undefined = this.storageService.getServer(serverName);
|
||||
if (!server)
|
||||
throw Error("Server not found");
|
||||
|
||||
console.log(`Connecting to ${serverName} (${server.serverURI})`);
|
||||
const connection = this.createConnection(serverName, server.serverURI);
|
||||
|
||||
//Save connection and return it
|
||||
this.activeConnections.push(connection);
|
||||
this.activeConnectionsChangedSubject$.next();
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish WS connection
|
||||
* @param serverUri WebSockets URI
|
||||
* @returns Created AnonimousSubject for this server
|
||||
*/
|
||||
private createConnection(serverName: string, serverUri: string): ActiveConnectionDto {
|
||||
const ws = new WebSocket(serverUri);
|
||||
|
||||
const newConnection: ActiveConnectionDto = {
|
||||
serverName: serverName,
|
||||
subject$: new Subject<WebSocketResponse>(),
|
||||
connectionStatus: ConnectionStatusEnum.Connecting,
|
||||
receivedMessages: [],
|
||||
sentCommands: [],
|
||||
isLoggedIn: false
|
||||
}
|
||||
|
||||
ws.onopen = (ev) => newConnection.connectionStatus = ConnectionStatusEnum.Connected;
|
||||
ws.onerror = (err) => newConnection.subject$.error(err);
|
||||
ws.onclose = () => this.closeConnection(serverName);
|
||||
ws.onmessage = (msg) => {
|
||||
//Parse raw message to an actual Object
|
||||
let parsedResponse: WebSocketResponse = this.parseResponse(msg, newConnection);
|
||||
//Save response
|
||||
newConnection.receivedMessages.push(parsedResponse);
|
||||
//Emit to subscribers
|
||||
newConnection.subject$.next(parsedResponse);
|
||||
}
|
||||
|
||||
//Store WebSocket client
|
||||
this.webSocketClients[serverName] = ws;
|
||||
|
||||
//Return connection
|
||||
return newConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives raw message from server and parses it to a actual WebSocketResponse object
|
||||
* @param response
|
||||
*/
|
||||
private parseResponse(response: MessageEvent<string>, newConnection: ActiveConnectionDto): WebSocketResponse {
|
||||
let parsedJson = JSON.parse(response.data) as WebSocketResponse;
|
||||
switch (parsedJson.status) {
|
||||
case 10:
|
||||
//Console output
|
||||
return parsedJson as ConsoleOutputResponse;
|
||||
case 200:
|
||||
//LoggedIn
|
||||
const r = parsedJson as LoggedInResponse;
|
||||
newConnection.isLoggedIn = true;
|
||||
newConnection.token = r.token;
|
||||
|
||||
if (this.storageService.getSetting(SettingsEnum.RetrieveLogFile))
|
||||
this.sendMessage(newConnection.serverName, WebSocketCommandEnum.ReadLogFile);
|
||||
return r;
|
||||
case 400:
|
||||
//Unknown
|
||||
return parsedJson as UnknownCommandResponse;
|
||||
case 401:
|
||||
//Login Required
|
||||
return parsedJson as LoginRequiredResponse;
|
||||
case 1000:
|
||||
//Players
|
||||
return parsedJson as PlayersResponse;
|
||||
case 1001:
|
||||
//CPU Usage
|
||||
return parsedJson as CpuResponse;
|
||||
case 1002:
|
||||
//RAM usage
|
||||
return parsedJson as RamResponse;
|
||||
case 1003:
|
||||
//TPS
|
||||
return parsedJson as TpsResponse;
|
||||
default:
|
||||
//Not recognised response
|
||||
console.error("Unrecognised response:", response);
|
||||
return parsedJson;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to a given server
|
||||
* @param serverName Name of the server the command is being sent to
|
||||
* @param command Command to send
|
||||
*/
|
||||
public sendMessage(serverName: string, commandType: WebSocketCommandEnum, params?: string): void {
|
||||
//Get ActiveConnection from array
|
||||
const serverConnection = this.activeConnections.find(e => e.serverName === serverName);
|
||||
if (!serverConnection)
|
||||
throw Error(`ActiveConnection not found for server ${serverName} whilst trying to send a message.`);
|
||||
|
||||
//Get WebSocket client from array. If not found, throw error
|
||||
const ws = this.webSocketClients[serverName];
|
||||
if (!ws)
|
||||
throw Error(`WebSocket client not found for server ${serverName} whilst trying to send a message.`);
|
||||
|
||||
//Build and send command if socket is open.
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
const command: WebSocketCommand = {
|
||||
command: commandType,
|
||||
params: params,
|
||||
token: serverConnection.token
|
||||
}
|
||||
ws.send(JSON.stringify(command));
|
||||
serverConnection.sentCommands.push(command);
|
||||
} else {
|
||||
console.error(`Message to ${serverName} NOT sent because socket is not open yet.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close connection with a server. This method needs to be here in order to be able to modify activeConnections$
|
||||
* @param serverName Server name which wants connection to be closed
|
||||
*/
|
||||
public closeConnection(serverName: string): void {
|
||||
const serverConnection = this.activeConnections.find(e => e.serverName === serverName);
|
||||
|
||||
if (serverConnection) {
|
||||
serverConnection.subject$.complete();
|
||||
serverConnection.connectionStatus = ConnectionStatusEnum.Disconnected;
|
||||
this.webSocketClients[serverName] = undefined;
|
||||
this.activeConnections = this.activeConnections.filter(e => e.serverName !== serverName);
|
||||
this.activeConnectionsChangedSubject$.next();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user