Brand new Angular client

This commit is contained in:
Carlos
2022-06-15 20:24:56 +02:00
parent 544720331b
commit 7d13d219d1
109 changed files with 23573 additions and 1892 deletions

View 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();
}
}

View 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
}

View 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();
}
}
}