Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
53e0d42ea2 | ||
![]() |
a942ce74a7 | ||
![]() |
264e6214b3 | ||
![]() |
2881e93f36 | ||
![]() |
6fd006280e | ||
![]() |
652a81bce4 | ||
![]() |
ef7ebdc219 | ||
![]() |
f7b0ab9033 | ||
![]() |
b7b8608e06 | ||
![]() |
ad6815636e | ||
![]() |
106425066e | ||
![]() |
7c9452408f | ||
![]() |
d178cd1982 | ||
![]() |
0265584394 | ||
![]() |
aefba0ccbb | ||
![]() |
c453ff88de | ||
![]() |
4d3e8a4af3 | ||
![]() |
de292ddd8c | ||
![]() |
07c72acaef | ||
![]() |
005f3de91e | ||
![]() |
527d5b0a1e | ||
![]() |
30b84bd7d2 | ||
![]() |
316e8f304a | ||
![]() |
ec317781b8 | ||
![]() |
394520eeb3 | ||
![]() |
38175c2b6b | ||
![]() |
bb3cb96e48 | ||
![]() |
af9680c91c | ||
![]() |
f0d6372451 | ||
![]() |
b3cd757840 | ||
![]() |
16cfe88733 | ||
![]() |
c12aa56f2c | ||
![]() |
3d72ed08d4 | ||
![]() |
509bd66889 | ||
![]() |
2295ad67a4 | ||
![]() |
7dfd2081d9 | ||
![]() |
30aedbc199 | ||
![]() |
d8c9277a77 | ||
![]() |
576718fe00 | ||
![]() |
aa9f3773b2 | ||
![]() |
5fcac0f70c | ||
![]() |
8952674361 | ||
![]() |
8fae3fa0b9 | ||
![]() |
2d4a9014fb | ||
![]() |
de21e8557d | ||
![]() |
6c44709055 | ||
![]() |
abd8df6e14 | ||
![]() |
bbadafed12 | ||
![]() |
ca6894c74c | ||
![]() |
bff643f0d9 | ||
![]() |
933aae6c13 | ||
![]() |
3435c1e07b | ||
![]() |
7d13d219d1 |
100
.github/workflows/codeql-analysis.yml
vendored
100
.github/workflows/codeql-analysis.yml
vendored
@ -3,69 +3,93 @@
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '0 6 * * 6'
|
||||
- cron: '20 12 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['javascript', 'java']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
|
||||
include:
|
||||
- language: java-kotlin
|
||||
build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too.
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -72,3 +72,8 @@ local.properties
|
||||
|
||||
#idea
|
||||
.idea
|
||||
src/es/mesacarlos/webconsole/.DS_Store
|
||||
src/.DS_Store
|
||||
src/es/.DS_Store
|
||||
src/es/mesacarlos/.DS_Store
|
||||
.DS_Store
|
||||
|
70
README.md
70
README.md
@ -4,7 +4,7 @@
|
||||
[](https://github.com/mesacarlos/WebConsole/releases/latest)
|
||||

|
||||
|
||||
WebConsole is a Spigot plugin for Minecraft 1.8-1.18+ that allows you to view your server console and manage your server from anywhere. It creates a WebSocket server in the background used by the web interface to send commands, receive your console log and manage your server.
|
||||
WebConsole is a Spigot plugin for Minecraft 1.8-1.21+ that allows you to view your server console and manage your server from anywhere. It creates a WebSocket server in the background used by the web interface to send commands, receive your console log and manage your server.
|
||||
|
||||
Don't worry about privacy or security: all data is stored in your browser locally and your PC will connect directly to your minecraft server. No intermediary web servers, just you and your MC server.
|
||||
|
||||
@ -14,61 +14,63 @@ Don't worry about privacy or security: all data is stored in your browser locall
|
||||
* Colors supported, for both Windows and Linux hosts. (Colors are represented different in each platform).
|
||||
* Real-time connected players, machine CPU and server RAM usage information.
|
||||
* Capable of keeping active connections to more than one server to keep retrieving console log in the background for them all.
|
||||
* English, Spanish, Chinese (thanks to Neubulae and OPhantomO), Czech (thanks to Tada), Deutsch (thanks to NoNamePro0), Dutch (thanks to Twockx), French (thanks to pickatchou999), Italian (thanks to AlexZap), Japanese (thanks to kuroneko6423), Korean (thanks to XxPKBxX), Portuguese (thanks to AlexandreMuassab and Connect500BR), Russian (thanks to Stashenko) and Turkish (thanks to acarnd03) supported.
|
||||
* English, Spanish, Traditional Chinese, Simplified Chinese, Czech, Deutsch, Dutch, French, Italian, Japanese, Korean, Portuguese, Russian and Turkish supported. Big shout-out to all contributors around the world that helped translating this project into all these languages!
|
||||
* Free!
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## Plugin installation
|
||||
## Step 1. Plugin installation
|
||||
|
||||
1. Download plugin from [Releases](https://github.com/mesacarlos/WebConsole/releases).
|
||||
2. Open and close your server to generate the config.yml file and open it. You will see something like the following:
|
||||
|
||||
useSSL: false
|
||||
StoreType: JKS
|
||||
KeyStore: plugins/WebConsole/keystore.jks
|
||||
StorePassword: storepassword
|
||||
KeyPassword: keypassword
|
||||
host: 0.0.0.0
|
||||
port: 8080
|
||||
language: en
|
||||
passwords:
|
||||
admin:
|
||||
user1:
|
||||
password: mySecurePassword
|
||||
commandWhitelist:
|
||||
enabled: true
|
||||
commandWhitelistActsAsBlacklist: false
|
||||
whitelist:
|
||||
- whisper
|
||||
- gamemode survival
|
||||
viewer: {}
|
||||
|
||||
```
|
||||
useSSL: false
|
||||
StoreType: JKS
|
||||
KeyStore: plugins/WebConsole/keystore.jks
|
||||
StorePassword: storepassword
|
||||
KeyPassword: keypassword
|
||||
host: 0.0.0.0
|
||||
port: 8080
|
||||
language: en
|
||||
passwords:
|
||||
admin:
|
||||
user1:
|
||||
password: mySecurePassword
|
||||
commandWhitelist:
|
||||
enabled: true
|
||||
commandWhitelistActsAsBlacklist: false
|
||||
whitelist:
|
||||
- whisper
|
||||
- gamemode survival
|
||||
viewer: {}
|
||||
```
|
||||
|
||||
|
||||
A explanation of the `host`, `port`, `language` and `passwords` fields follows:
|
||||
|
||||
`host`: Leaving it as 0.0.0.0 will do the trick. If you experience issues , you can change this value to your device IP. If you are in a VPS or dedicated server (or you have a full public IP allocated for your device) type your public IP. If you are at your home (and you don't have a public IP assigned to your device) type your private IP, it is probably something like 192.168.xx.xx.
|
||||
`host`: Leaving it as 0.0.0.0 will do the trick. If you experience issues, you can change this value to your device IP. If you are in a VPS or dedicated server (or you have a full public IP allocated for your device) type your public IP. If you are at your home (and you don't have a public IP assigned to your device) type your private IP, it is probably something like 192.168.xx.xx.
|
||||
|
||||
`port`: A port where to run this plugin (cannot be the port you are using for Minecraft).
|
||||
|
||||
You can modify `language` to view console and command messages in your preferred language. Valid languages are English (`en`), Spanish (`es`), Chinese (`zh`), Czech (`cs`), Deutsch (`de`), Dutch (`nl`), French (`fr`), Italian (`it`), Japanese (`ja`) Korean (`ko`), Polskie (`pl`), Portuguese (`pt`), Russian (`ru`) and Turkish (`tr`). **IMPORTANT: There is a known issue with Microsoft Windows cmd that shows weird characters when using a language different than English. If you are using Windows to host your server, check [this wiki page](https://github.com/mesacarlos/WebConsole/wiki/Show-local-characters-in-Windows-Console) to solve the problem**.
|
||||
You can modify `language` to view console and command messages in your preferred language. Valid languages are English (`en`), Spanish (`es`), Traditional Chinese (`zh_TW`), Simplified Chinese (`zh_CN`), Czech (`cs`), Deutsch (`de`), Dutch (`nl`), French (`fr`), Italian (`it`), Japanese (`ja`) Korean (`ko`), Polskie (`pl`), Portuguese (`pt`), Russian (`ru`) and Turkish (`tr`). **IMPORTANT: There is a known issue with Microsoft Windows cmd that shows weird characters when using a language different than English. If you are using Windows to host your server, check [this wiki page](https://github.com/mesacarlos/WebConsole/wiki/Show-local-characters-in-Windows-Console) to solve the problem**.
|
||||
|
||||
From version 2.0 you can now create more than one user and set them as admin (Permission for both reading console and executing commands) or viewer (Permission for only reading console and CPU and RAM usage). This is configured using the `passwords` section:
|
||||
Since version 2.0 you can now create more than one user and set them as admin (Permission for both reading console and executing commands) or viewer (Permission for only reading console and CPU and RAM usage). This is configured using the `passwords` section:
|
||||
- By default, a user called user1 with password mySecurePassword is created, please replace or remove it as it is only served as an example.
|
||||
- If you want to create a view-only user, remove the `{}` after `viewer: ` and type below a row like `user: password` replacing user with your desired username and password with your password.
|
||||
- If you want to create a view-only user, remove the `{}` after `viewer: ` and type below a row like `user: password` replacing user with your desired username and password with your password. ([Example](https://github.com/mesacarlos/WebConsole/issues/140))
|
||||
- You can create as many admins or viewers as needed.
|
||||
- For all your admin users, you can enable a whitelist of commands under the commandWhitelist section of your user.
|
||||
|
||||
The rest of the fields are used for SSL configuration. You can learn how to activate SSL [here](https://github.com/mesacarlos/WebConsole/wiki/SSL-Configuration). SSL **is not** required for WebConsole to work, you can still use it without encription, unless you are hosting your client in a HTTPS server, in this case is mandatory to enable SSL in all your servers due to web browsers restrictions.
|
||||
The rest of the fields are used for SSL configuration. You can learn how to activate SSL [here](https://github.com/mesacarlos/WebConsole/wiki/SSL-Configuration). SSL **is not** required for WebConsole to work, you can still use it without encription, unless you are hosting your client in a HTTPS server, in this case is mandatory to enable SSL in all your servers due to web browsers security restrictions.
|
||||
|
||||
|
||||
## Using web interface
|
||||
|
||||
1. You can download web interface (client.zip) from [Releases](https://github.com/mesacarlos/WebConsole/releases) or, if you prefer, you can use my [WebConsole Hosted Clients](https://mesacarlos.es/webconsole/) webpage. You can host the client in a web server, or use it offline. That's up to you.
|
||||
2. Open index.html if you downloaded the client or click on the latest version if you are using my Hosted Clients website.
|
||||
3. To start adding servers, click on `Your servers`, and then `Add server`. Fill Server name, IP and WebConsole port (the one you placed into config.yml before), and you are ready to go. You will be prompted for password when connecting.
|
||||
## Step 2. Using web interface
|
||||
You can either download and host your own client or use it from my server.
|
||||
- To host your own client, download client.zip from [Releases](https://github.com/mesacarlos/WebConsole/releases) and drop these files in a web server, like Apache, NGINX, or other of your liking.
|
||||
- If you don't want to deploy your own client, you can also use my [WebConsole Hosted Clients](https://mesacarlos.es/webconsole/) webpage, where all versions of the client are available.
|
||||
|
||||
## Check connected WebConsole clients
|
||||
Since v1.3, you can use /WebConsole command to view how many clients are connected and their IP address. This is the only Minecraft command provided by this plugin. This command requires you to have `webconsole.webconsole` permission to execute it.
|
||||
|
16
client/.browserslistrc
Normal file
16
client/.browserslistrc
Normal file
@ -0,0 +1,16 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
16
client/.editorconfig
Normal file
16
client/.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
42
client/.gitignore
vendored
Normal file
42
client/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
4
client/.vscode/extensions.json
vendored
Normal file
4
client/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
20
client/.vscode/launch.json
vendored
Normal file
20
client/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
42
client/.vscode/tasks.json
vendored
Normal file
42
client/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
111
client/angular.json
Normal file
111
client/angular.json
Normal file
@ -0,0 +1,111 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"WebConsoleClient": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
},
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/web-console-client",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "1mb",
|
||||
"maximumError": "10mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "20kb",
|
||||
"maximumError": "40kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "WebConsoleClient:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "WebConsoleClient:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "WebConsoleClient:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "WebConsoleClient"
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
@ -1,303 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="styles/WebConsole.css">
|
||||
|
||||
<title>WebConsole</title>
|
||||
</head>
|
||||
<body class="d-flex flex-column h-100">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="#" id="navbarBrandLink">WebConsole</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarContainer" aria-controls="navbarContainer" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarContainer">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="navbarHomeLink">Home</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="serversDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Your servers
|
||||
</a>
|
||||
<div class="dropdown-menu" id="ServerListDropDown" aria-labelledby="serversDropdown">
|
||||
<a class="dropdown-item" href data-toggle="modal" data-target="#addServerModal" id="add_server">Add Server</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item servermenuitem" href="#">Example server</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="lang_dropdown">
|
||||
Language
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="langDropdown">
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('en_US')">English</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('es_ES')">Español</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('zh_CN')">中文</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('ko_KR')">한국어</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('cs_CZ')">Czech</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('de_DE')">Deutsche</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('nl_NL')">Dutch</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('fr_FR')">Français</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('it_IT')">Italiano</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('pt_BR')">Português</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('pl_PL')">Polskie</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('ru_RU')">русский</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('tr_TR')">Türk</a>
|
||||
<a class="dropdown-item" href="#" onclick="setLanguage('ja_JA')">日本語</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href data-toggle="modal" data-target="#settingsModal" id="settingsLink">Settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Begin page content -->
|
||||
<main role="main" class="flex-shrink-0">
|
||||
<!-- Welcome container -->
|
||||
<div class="container" id="welcomeContainer">
|
||||
<h1 class="mt-5" id="home_header">Select a server from the menu</h1>
|
||||
<p class="lead" id="home_description">Use the navigation bar to add a new Minecraft Server or connect to a previously added one.</p>
|
||||
</div>
|
||||
|
||||
<!-- Server management container -->
|
||||
<div class="container" id="serverContainer" style="display: none;">
|
||||
<h1 class="mt-4" id="serverTitle"></h1>
|
||||
|
||||
<div class="row p-2">
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" id="players_online">Players Online</h5>
|
||||
<p class="card-text"><span id="connectedPlayers">0</span> / <span id="maxPlayers">Unknown</span></p>
|
||||
<div class="progress flat-progressbar">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" id="playerProgressBar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" id="cpu_title">CPU</h5>
|
||||
<p class="card-text" id="cpuInfo">0%</p>
|
||||
<div class="progress flat-progressbar">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" id="CpuProgressBar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" id="ram_title">RAM</h5>
|
||||
<p class="card-text"><span id="usedRam">0</span> MB / <span id="totalRam">0</span> MB</p>
|
||||
<div class="progress flat-progressbar">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" id="RamProgressBar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" id="tps_title">TPS</h5>
|
||||
<p class="card-text"><span id="tps">0</span> Ticks / <span id="maxTps">0</span> Ticks</p>
|
||||
<div class="progress flat-progressbar">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" id="TpsProgressBar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-2 mb-2">
|
||||
<div class="col-sm-9">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<span id="user_title">Logged as</span> <span id="loggedUsernameLabel">Unknown</span> (<span id="loggedUserTypeLabel">Unknown</span>)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<button type="button" class="btn btn-danger btn-sm" id="deleteServerButton">Delete server</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-2">
|
||||
<div class="card-body overflow-auto text-light bg-dark console" id="consoleTextArea"></div>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="commandInput">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" id="sendCommandButton">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Add Server Modal -->
|
||||
<div class="modal fade" id="addServerModal" tabindex="-1" role="dialog" aria-labelledby="addServerModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addServerModalLongTitle">Add a new server</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="CloseAddServer">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addServerForm">
|
||||
<div class="form-group">
|
||||
<label for="server-name" class="col-form-label" id="addServerModalSvName">Server name:</label>
|
||||
<input type="text" class="form-control" id="server-name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server-ip" class="col-form-label" id="addServerModalSvIp">Server IP:</label>
|
||||
<input type="text" class="form-control" id="server-ip" placeholder="localhost" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server-port" class="col-form-label" id="addServerModalSvPort">Server port:</label>
|
||||
<input type="number" class="form-control" id="server-port" placeholder="8080" min="1" max="65535" required>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="server-ssl">
|
||||
<label class="form-check-label" for="server-ssl" id="addServerModalSvSsl">Server is SSL enabled</label>
|
||||
<div class="text-danger" id="addServerModalSslAdvice"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="addServerModalClose">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="saveAndConnectServerButton">Save and connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Modal -->
|
||||
<div class="modal fade" id="passwordModal" tabindex="-1" role="dialog" aria-labelledby="passwordModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="passwordModalLongTitle">Password required</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="passwordForm">
|
||||
<div class="form-group">
|
||||
<label for="server-pwd" class="col-form-label" id="passwordModalLabel">Password:</label>
|
||||
<input type="password" class="form-control" id="server-pwd">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="rememberPwdCheckbox">
|
||||
<label class="form-check-label" for="rememberPwdCheckbox" id="passwordModalRememberLabel">Remember password</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="passwordModalCloseButton">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal" id="passwordSendButton">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disconnection Modal -->
|
||||
<div class="modal fade" id="disconnectionModal" tabindex="-1" role="dialog" aria-labelledby="disconnectionModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="disconnectionModalLongTitle">Disconnected</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span id="disconnectionModalDescription">Connection was lost with the server. This can be caused by:</span>
|
||||
<ul>
|
||||
<li id="disconnectionModalsub1">Server was closed intentionally.</li>
|
||||
<li id="disconnectionModalsub2">Port is not opened on your host. In this case, troubleshoot using <a href="https://www.yougetsignal.com/tools/open-ports/">this tool</a> and recheck your firewall or router.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal" id="disconnectionModalCloseButton">Close</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="disconnectionModalWelcomeScreenButton">Welcome screen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Modal -->
|
||||
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="settingsModalLongTitle">WebConsole Settings</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="settingsModalDescription">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="showDateSettingsSwitch">
|
||||
<label class="custom-control-label" for="showDateSettingsSwitch" id="showDateSettingsSwitchLabel">Show date and time on each console line</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="readLogFileSwitch">
|
||||
<label class="custom-control-label" for="readLogFileSwitch" id="readLogFileSwitchLabel">Retrieve full log file from server after login</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="settingsModalCloseButton">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Webpage footer -->
|
||||
<footer class="footer mt-auto py-3">
|
||||
<div class="container">
|
||||
<span class="text-muted">WebConsole v2.4 - <a href="https://github.com/mesacarlos/WebConsole">GitHub</a></span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- WebConsole JS Objects -->
|
||||
<script src="scripts/object/Setting.js?v=2.4.0"></script>
|
||||
<script src="scripts/object/WSServer.js?v=2.4.0"></script>
|
||||
|
||||
<!-- WebConsole JS Scripts -->
|
||||
<script src="scripts/WebConsoleLanguage.js?v=2.4.0"></script>
|
||||
<script src="scripts/WebConsoleConnector.js?v=2.4.0"></script>
|
||||
<script src="scripts/WebConsoleManager.js?v=2.4.0"></script>
|
||||
<script src="scripts/WebConsolePersistenceManager.js?v=2.4.0"></script>
|
||||
<script src="scripts/WebConsole.js?v=2.4.0"></script>
|
||||
<script src="scripts/WebConsoleJqueryHandler.js?v=2.4.0"></script>
|
||||
</body>
|
||||
</html>
|
44
client/karma.conf.js
Normal file
44
client/karma.conf.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/web-console-client'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
12416
client/package-lock.json
generated
Normal file
12416
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
51
client/package.json
Normal file
51
client/package.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "web-console-client",
|
||||
"version": "2.7.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.3.0",
|
||||
"@angular/common": "~13.3.0",
|
||||
"@angular/compiler": "~13.3.0",
|
||||
"@angular/core": "~13.3.0",
|
||||
"@angular/forms": "~13.3.0",
|
||||
"@angular/localize": "~13.3.0",
|
||||
"@angular/platform-browser": "~13.3.0",
|
||||
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||
"@angular/router": "~13.3.0",
|
||||
"@fortawesome/angular-fontawesome": "^0.10.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^12.1.2",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"@ngx-translate/http-loader": "^7.0.0",
|
||||
"@popperjs/core": "^2.10.2",
|
||||
"ansi_up": "^6.0.2",
|
||||
"bootstrap": "^5.1.3",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~13.3.7",
|
||||
"@angular/cli": "~13.3.7",
|
||||
"@angular/compiler-cli": "~13.3.0",
|
||||
"@types/jasmine": "~3.10.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"jasmine-core": "~4.0.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.1.0",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.6.2"
|
||||
}
|
||||
}
|
@ -1,344 +0,0 @@
|
||||
/**
|
||||
Main JS file for WebConsole.
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Global variables
|
||||
*/
|
||||
const persistenceManager = new WebConsolePersistenceManager();
|
||||
const connectionManager = new WebConsoleManager();
|
||||
let lang;
|
||||
let autoPasswordCompleted = false; //When true, saved password was used. If a 401 is received, then saved password is not correct
|
||||
let statusCommandsInterval = -1;
|
||||
let commandHistoryIndex = -1; //Saves current command history index. -1 when not browsing history.
|
||||
|
||||
/**
|
||||
* Load list of servers in file servers.json
|
||||
* and auto update in next request when file is changed
|
||||
*/
|
||||
function readServerList() {
|
||||
let hash = persistenceManager.getSetting('server:hash')
|
||||
|
||||
/**
|
||||
* Hash code function used for compare version of file servers.json
|
||||
* https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
|
||||
*/
|
||||
const hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
|
||||
|
||||
fetch('servers.json')
|
||||
.then(res => res.text())
|
||||
.then(json => {
|
||||
if (hash !== hashCode(json)) {
|
||||
persistenceManager.setSetting('server:hash', hashCode(json))
|
||||
JSON.parse(json).forEach(server => persistenceManager.saveServer(server))
|
||||
}
|
||||
})
|
||||
.then(updateServerList)
|
||||
.catch(() => console.info('Ignore load new list in file servers.json.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and show server to user
|
||||
*/
|
||||
function openServer(serverName){
|
||||
//Hide welcome div if user is not in welcome page
|
||||
$("#welcomeContainer").hide();
|
||||
$("#serverContainer").show();
|
||||
|
||||
//Change server name and related info
|
||||
$("#serverTitle").text(serverName);
|
||||
$("#consoleTextArea").text("");
|
||||
$("#commandInput").prop("disabled", false);
|
||||
$("#sendCommandButton").prop("disabled", false);
|
||||
|
||||
//New server, new variables:
|
||||
autoPasswordCompleted = false;
|
||||
commandHistoryIndex = -1; //Reset command history index
|
||||
|
||||
//Create or retrieve connection
|
||||
connectionManager.loadConnection(serverName);
|
||||
|
||||
//Load saved messages
|
||||
let i;
|
||||
const messages = connectionManager.activeConnection.messages;
|
||||
for(i = 0; i < messages.length; i++){
|
||||
if(messages[i].status !== 401){
|
||||
onWebSocketsMessage(messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Subscribe a function
|
||||
connectionManager.activeConnection.subscribe(onWebSocketsMessage);
|
||||
}
|
||||
|
||||
function onWebSocketsMessage(message){
|
||||
switch (message.status) {
|
||||
case 10:
|
||||
//Console Output
|
||||
writeToWebConsole(message.message, message.time);
|
||||
break;
|
||||
case 200:
|
||||
//LoggedIn
|
||||
writeToWebConsole(message.message);
|
||||
|
||||
//Show user and permissions
|
||||
$("#loggedUsernameLabel").text(message.username);
|
||||
$("#loggedUserTypeLabel").text(message.as);
|
||||
|
||||
//Disable command bar if user is viewer
|
||||
if(message.as.toLowerCase() === "viewer"){
|
||||
$("#commandInput").prop("disabled", true);
|
||||
$("#sendCommandButton").prop("disabled", true);
|
||||
}
|
||||
|
||||
//Read log file if enabled
|
||||
if(connectionManager.activeConnection.isLogged === false){
|
||||
connectionManager.activeConnection.isLogged = true;
|
||||
if(persistenceManager.getSetting("retrieveLogFile") === true)
|
||||
connectionManager.askForLogs();
|
||||
}
|
||||
break;
|
||||
case 400:
|
||||
//Unknown Command
|
||||
writeToWebConsole(message.message);
|
||||
break;
|
||||
case 401:
|
||||
//Waiting for login. Show password modal or retrieve password
|
||||
const savedPwd = persistenceManager.getServer(connectionManager.activeConnection.serverName).serverPassword;
|
||||
if(typeof savedPwd !== "undefined" && !autoPasswordCompleted){
|
||||
connectionManager.sendPassword(savedPwd);
|
||||
autoPasswordCompleted = true;
|
||||
}else{
|
||||
$('#passwordModal').modal('show');
|
||||
}
|
||||
break;
|
||||
case 1000:
|
||||
//Players
|
||||
writePlayerInfo(message.connectedPlayers, message.maxPlayers);
|
||||
connectionManager.activeConnection.players = JSON.parse(message.players);
|
||||
break;
|
||||
case 1001:
|
||||
//Cpu Usage
|
||||
writeCpuInfo(message.usage);
|
||||
break;
|
||||
case 1002:
|
||||
//RAM Usage
|
||||
writeRamInfo(message.free, message.used, message.max);
|
||||
break;
|
||||
case 1003:
|
||||
//Server TPS
|
||||
writeTpsInfo(message.tps, 20);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown server response:');
|
||||
}
|
||||
console.log(message);
|
||||
|
||||
//Add interval for Players, CPU and RAM info, if not set
|
||||
if(statusCommandsInterval === -1 && message.status !== 401){
|
||||
statusCommandsInterval = setInterval(function(){
|
||||
connectionManager.askForInfo();
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to console
|
||||
*/
|
||||
function writeToWebConsole(msg, time){
|
||||
const isScrolledDown = document.getElementById("consoleTextArea").scrollHeight - document.getElementById("consoleTextArea").scrollTop - 40 === $("#consoleTextArea").height();
|
||||
|
||||
//Write to div, replacing < to < (to avoid XSS) and replacing new line to br.
|
||||
msg = msg.replace(/</g, "<");
|
||||
msg = msg.replace(/(?:\r\n|\r|\n)/g, "<br>");
|
||||
|
||||
//Color filter for Windows (thanks to SuperPykkon)
|
||||
msg = msg.replace(/\[0;30;22m/g, "<span style='color: #000000;'>"); //&0
|
||||
msg = msg.replace(/\[0;34;22m/g, "<span style='color: #0000AA;'>"); //&1
|
||||
msg = msg.replace(/\[0;32;22m/g, "<span style='color: #00AA00;'>"); //&2
|
||||
msg = msg.replace(/\[0;36;22m/g, "<span style='color: #00AAAA;'>"); //&3
|
||||
msg = msg.replace(/\[0;31;22m/g, "<span style='color: #AA0000;'>"); //&4
|
||||
msg = msg.replace(/\[0;35;22m/g, "<span style='color: #AA00AA;'>"); //&5
|
||||
msg = msg.replace(/\[0;33;22m/g, "<span style='color: #FFAA00;'>"); //&6
|
||||
msg = msg.replace(/\[0;37;22m/g, "<span style='color: #AAAAAA;'>"); //&7
|
||||
msg = msg.replace(/\[0;30;1m/g, "<span style='color: #555555;'>"); //&8
|
||||
msg = msg.replace(/\[0;34;1m/g, "<span style='color: #5555FF;'>"); //&9
|
||||
msg = msg.replace(/\[0;32;1m/g, "<span style='color: #55FF55;'>"); //&a
|
||||
msg = msg.replace(/\[0;36;1m/g, "<span style='color: #55FFFF;'>"); //&b
|
||||
msg = msg.replace(/\[0;31;1m/g, "<span style='color: #FF5555;'>"); //&c
|
||||
msg = msg.replace(/\[0;35;1m/g, "<span style='color: #FF55FF;'>"); //&d
|
||||
msg = msg.replace(/\[0;33;1m/g, "<span style='color: #FFFF55;'>"); //&e
|
||||
msg = msg.replace(/\[0;37;1m/g, "<span style='color: #FFFFFF;'>"); //&f
|
||||
msg = msg.replace(/\[m/g, "</span>"); //&f
|
||||
|
||||
//Color filter for UNIX (This is easier!)
|
||||
//span may not be closed every time but browsers will do for ourselves
|
||||
msg = msg.replace(/§0/g, "<span style='color: #000000;'>"); //&0
|
||||
msg = msg.replace(/§1/g, "<span style='color: #0000AA;'>"); //&1
|
||||
msg = msg.replace(/§2/g, "<span style='color: #00AA00;'>"); //&2
|
||||
msg = msg.replace(/§3/g, "<span style='color: #00AAAA;'>"); //&3
|
||||
msg = msg.replace(/§4/g, "<span style='color: #AA0000;'>"); //&4
|
||||
msg = msg.replace(/§5/g, "<span style='color: #AA00AA;'>"); //&5
|
||||
msg = msg.replace(/§6/g, "<span style='color: #FFAA00;'>"); //&6
|
||||
msg = msg.replace(/§7/g, "<span style='color: #AAAAAA;'>"); //&7
|
||||
msg = msg.replace(/§8/g, "<span style='color: #555555;'>"); //&8
|
||||
msg = msg.replace(/§9/g, "<span style='color: #5555FF;'>"); //&9
|
||||
msg = msg.replace(/§a/g, "<span style='color: #55FF55;'>"); //&a
|
||||
msg = msg.replace(/§b/g, "<span style='color: #55FFFF;'>"); //&b
|
||||
msg = msg.replace(/§c/g, "<span style='color: #FF5555;'>"); //&c
|
||||
msg = msg.replace(/§d/g, "<span style='color: #FF55FF;'>"); //&d
|
||||
msg = msg.replace(/§e/g, "<span style='color: #FFFF55;'>"); //&e
|
||||
msg = msg.replace(/§f/g, "<span style='color: #FFFFFF;'>"); //&f
|
||||
|
||||
msg = msg.replace(/§l/g, "<span style='font-weight:bold;'>"); //&l
|
||||
msg = msg.replace(/§m/g, "<span style='text-decoration: line-through;'>"); //&m
|
||||
msg = msg.replace(/§n/g, "<span style='text-decoration: underline;'>"); //&n
|
||||
msg = msg.replace(/§o/g, "<span style='font-style: italic;'>"); //&o
|
||||
|
||||
msg = msg.replace(/§r/g, "</span>"); //&r
|
||||
|
||||
//Color filter for MC 1.18 (Also easy :D)
|
||||
//span may not be closed every time but browsers will do for ourselves
|
||||
msg = msg.replace(/0/g, "<span style='color: #000000;'>"); //&0
|
||||
msg = msg.replace(/1/g, "<span style='color: #0000AA;'>"); //&1
|
||||
msg = msg.replace(/2/g, "<span style='color: #00AA00;'>"); //&2
|
||||
msg = msg.replace(/3/g, "<span style='color: #00AAAA;'>"); //&3
|
||||
msg = msg.replace(/4/g, "<span style='color: #AA0000;'>"); //&4
|
||||
msg = msg.replace(/5/g, "<span style='color: #AA00AA;'>"); //&5
|
||||
msg = msg.replace(/6/g, "<span style='color: #FFAA00;'>"); //&6
|
||||
msg = msg.replace(/7/g, "<span style='color: #AAAAAA;'>"); //&7
|
||||
msg = msg.replace(/8/g, "<span style='color: #555555;'>"); //&8
|
||||
msg = msg.replace(/9/g, "<span style='color: #5555FF;'>"); //&9
|
||||
msg = msg.replace(/a/g, "<span style='color: #55FF55;'>"); //&a
|
||||
msg = msg.replace(/b/g, "<span style='color: #55FFFF;'>"); //&b
|
||||
msg = msg.replace(/c/g, "<span style='color: #FF5555;'>"); //&c
|
||||
msg = msg.replace(/d/g, "<span style='color: #FF55FF;'>"); //&d
|
||||
msg = msg.replace(/e/g, "<span style='color: #FFFF55;'>"); //&e
|
||||
msg = msg.replace(/f/g, "<span style='color: #FFFFFF;'>"); //&f
|
||||
|
||||
msg = msg.replace(/l/g, "<span style='font-weight:bold;'>"); //&l
|
||||
msg = msg.replace(/m/g, "<span style='text-decoration: line-through;'>"); //&m
|
||||
msg = msg.replace(/n/g, "<span style='text-decoration: underline;'>"); //&n
|
||||
msg = msg.replace(/o/g, "<span style='font-style: italic;'>"); //&o
|
||||
|
||||
msg = msg.replace(/r/g, "</span>"); //&r
|
||||
|
||||
//Append datetime if enabled
|
||||
if(persistenceManager.getSetting("dateTimePrefix")){
|
||||
if(typeof time !== 'undefined' && time !== null) //if time is present and not null
|
||||
msg = "[" + time + "] " + msg;
|
||||
else if(typeof time !== 'undefined' && time === null) //if time is present and null
|
||||
; //no time (is already printed)
|
||||
else
|
||||
msg = "[" + new Date().toLocaleTimeString() + "] " + msg;
|
||||
}
|
||||
|
||||
|
||||
$("#consoleTextArea").append(msg + "<br>");
|
||||
|
||||
if(isScrolledDown){
|
||||
const textarea = document.getElementById('consoleTextArea');
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill connected players card
|
||||
*/
|
||||
function writePlayerInfo(connected, maximum){
|
||||
$("#connectedPlayers").text(connected);
|
||||
$("#maxPlayers").text(maximum);
|
||||
|
||||
const percent = (connected / maximum) * 100;
|
||||
$("#playerProgressBar").width(percent + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill CPU info card
|
||||
*/
|
||||
function writeCpuInfo(usage){
|
||||
$("#cpuInfo").text(usage + "%");
|
||||
|
||||
$("#CpuProgressBar").width(usage + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill RAM info card
|
||||
*/
|
||||
function writeRamInfo(free, used, total){
|
||||
$("#usedRam").text(used);
|
||||
$("#totalRam").text(total);
|
||||
|
||||
const percent = (used / total) * 100;
|
||||
$("#RamProgressBar").width(percent + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill TPS info card
|
||||
*/
|
||||
function writeTpsInfo(tps, max){
|
||||
if(tps > 20) {
|
||||
tps = 20;
|
||||
}
|
||||
$("#tps").text(tps);
|
||||
$("#maxTps").text(max);
|
||||
|
||||
const percent = (tps / max) * 100;
|
||||
$("#TpsProgressBar").width(percent + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from WebConsoleConnector only.
|
||||
*/
|
||||
function closedConnection(serverName){
|
||||
if(connectionManager.activeConnection.serverName === serverName){
|
||||
//Disable command input and button
|
||||
$("#commandInput").prop("disabled", true);
|
||||
$("#sendCommandButton").prop("disabled", true);
|
||||
|
||||
//Inform user
|
||||
$('#disconnectionModal').modal('show');
|
||||
}
|
||||
connectionManager.deleteConnection(serverName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows welcome screen
|
||||
*/
|
||||
function backToHomepage(){
|
||||
//Stop gathering info from server
|
||||
clearInterval(statusCommandsInterval);
|
||||
statusCommandsInterval = -1;
|
||||
|
||||
//Reset command history index
|
||||
commandHistoryIndex = -1;
|
||||
|
||||
//Clear all server indicators
|
||||
writePlayerInfo(0, 0);
|
||||
writeCpuInfo(0);
|
||||
writeRamInfo(0, 0, 0);
|
||||
|
||||
$("#welcomeContainer").show();
|
||||
$("#serverContainer").hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update dropdown with saved server list
|
||||
*/
|
||||
function updateServerList(){
|
||||
//Delete all servers in dropdown
|
||||
$('.servermenuitem').remove();
|
||||
|
||||
//Add all servers
|
||||
const servers = persistenceManager.getAllServers();
|
||||
for(let i = 0; i < servers.length; i++){
|
||||
$('#ServerListDropDown').append('<a class="dropdown-item servermenuitem" href="#" onclick="openServer(\'' + servers[i].serverName + '\')">' + servers[i].serverName.replace(/</g,"<").replace(/>/g,">").replace(/'/g,"").replace(/"/g,"") + '</a>');
|
||||
}
|
||||
|
||||
//Show a "no servers" message when no servers are added
|
||||
if(servers.length === 0){
|
||||
$('#ServerListDropDown').append('<a class="dropdown-item servermenuitem disabled" href="#" id="noServersAdded">No servers added</a>');
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/**
|
||||
WebConsole Connector for WebConsole
|
||||
Used to connect to WebSocketsServer
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
class WebConsoleConnector {
|
||||
|
||||
constructor(serverName, serverURI) {
|
||||
this.serverName = serverName;
|
||||
this.serverURI = serverURI;
|
||||
this.token;
|
||||
this.subscribers = []; //List of functions called when a new message arrive
|
||||
this.messages = []; //All messages retrieved since connection start
|
||||
this.commands = []; //EXEC Commands sent by user to this server
|
||||
this.players = []; //Connected users.
|
||||
this.isLogged = false; //Is logged in with valid pasword or not
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to WebSocket
|
||||
*/
|
||||
connect(){
|
||||
var connector = this;
|
||||
this.websocket = new WebSocket(this.serverURI);
|
||||
this.websocket.onopen = function(evt) { connector.onOpen(evt) };
|
||||
this.websocket.onclose = function(evt) { connector.onClose(evt) };
|
||||
this.websocket.onmessage = function(evt) { connector.onMessage(evt) };
|
||||
this.websocket.onerror = function(evt) { connector.onError(evt) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function
|
||||
*/
|
||||
onOpen(evt){
|
||||
//TODO Check version is correct, and this websocket server is a WebConsole WebSocket
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function
|
||||
*/
|
||||
onClose(evt){
|
||||
closedConnection(this.serverName);
|
||||
console.log("Closed reason: " + evt.reason); //No reason provided (using chrome at least)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function
|
||||
*/
|
||||
onMessage(evt){
|
||||
var obj = JSON.parse(evt.data);
|
||||
|
||||
|
||||
if(obj.status === 200) //If is a LoggedIn response, save our token
|
||||
this.token = obj.token;
|
||||
|
||||
this.notify(obj); //Notify all subscribers
|
||||
this.messages.push(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function
|
||||
*/
|
||||
onError(evt){
|
||||
//TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a WebSocket command to Server
|
||||
*/
|
||||
sendToServer(message){
|
||||
this.websocket.send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies a new message to all subscribers
|
||||
*/
|
||||
notify(obj){
|
||||
this.subscribers.forEach(function(fun) {
|
||||
fun(obj); //Calls function with this object
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function to subscriber list
|
||||
*/
|
||||
subscribe(func){
|
||||
this.subscribers.push(func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe all subscribers
|
||||
*/
|
||||
removeSubscribers(){
|
||||
this.subscribers = [];
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
/**
|
||||
JS File containing all JQuery-related handlers
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Show saved serverlist on startup
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
$("#serverContainer").hide();
|
||||
persistenceManager.initializeSettings();
|
||||
setLanguage(persistenceManager.getLanguage());
|
||||
readServerList();
|
||||
updateServerList();
|
||||
|
||||
//Check SSL host
|
||||
if (location.protocol != 'https:'){
|
||||
$("#addServerModalSslAdvice").hide();
|
||||
}else{
|
||||
$("#server-ssl").prop('checked', true);
|
||||
$("#server-ssl").prop("disabled", true);
|
||||
}
|
||||
|
||||
//Remove servers from persistence with invalid names. See v1.4-rev2 for details
|
||||
var servers = persistenceManager.getAllServers();
|
||||
for(var i = 0; i < servers.length; i++){
|
||||
if(servers[i].serverName.includes("\'") || servers[i].serverName.includes("\"") || servers[i].serverName.includes("<") || servers[i].serverName.includes(">")){
|
||||
persistenceManager.deleteServer(servers[i].serverName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add server modal button click
|
||||
*/
|
||||
$("#saveAndConnectServerButton").click(function() {
|
||||
//Validate form data
|
||||
var addServerForm = document.getElementById("addServerForm");
|
||||
if(!addServerForm.checkValidity()){
|
||||
addServerForm.classList.add('was-validated');
|
||||
return;
|
||||
}
|
||||
|
||||
//Save server
|
||||
var name = $("#server-name").val().replace(/</g,"<").replace(/>/g,">").replace(/'/g,"").replace(/"/g,"");
|
||||
var wcIp = $("#server-ip").val();
|
||||
var wcPort = $("#server-port").val();
|
||||
var wcSsl = $("#server-ssl").prop('checked');
|
||||
var uri;
|
||||
if(wcSsl){
|
||||
uri = "wss://" + wcIp + ":" + wcPort;
|
||||
}else{
|
||||
uri = "ws://" + wcIp + ":" + wcPort;
|
||||
}
|
||||
persistenceManager.saveServer(new WSServer(name, uri));
|
||||
|
||||
//Close modal
|
||||
addServerForm.classList.remove('was-validated');
|
||||
$("#addServerModal").modal('hide');
|
||||
|
||||
//Empty all modal values
|
||||
$("#server-name").val("");
|
||||
$("#server-ip").val("");
|
||||
$("#server-port").val("");
|
||||
|
||||
//Update GUI serverlist
|
||||
updateServerList();
|
||||
|
||||
//Connect to server
|
||||
openServer(name);
|
||||
});
|
||||
|
||||
/**
|
||||
* Password modal button click
|
||||
*/
|
||||
$("#passwordSendButton").click(function() {
|
||||
//Close modal
|
||||
$('#passwordModal').modal('hide');
|
||||
});
|
||||
|
||||
/**
|
||||
* Password modal Enter key pressed
|
||||
*/
|
||||
$("#passwordForm").submit(function(event){
|
||||
//Solves bug with forms:
|
||||
event.preventDefault();
|
||||
|
||||
//Close modal
|
||||
$('#passwordModal').modal('hide');
|
||||
});
|
||||
|
||||
/**
|
||||
* On password modal close (Login)
|
||||
*/
|
||||
$('#passwordModal').on('hidden.bs.modal', function (e) {
|
||||
//Send LOGIN command to server
|
||||
var pwd = $("#server-pwd").val();
|
||||
connectionManager.sendPassword(pwd);
|
||||
|
||||
//Save password if set
|
||||
var savePasswordChecked = $("#rememberPwdCheckbox").prop("checked");
|
||||
if(savePasswordChecked){
|
||||
var serverName = connectionManager.activeConnection.serverName;
|
||||
var serverURI = connectionManager.activeConnection.serverURI;
|
||||
var svObj = new WSServer(serverName, serverURI);
|
||||
svObj.setPassword(pwd);
|
||||
persistenceManager.saveServer(svObj);
|
||||
}
|
||||
|
||||
//Remove password from modal
|
||||
$("#server-pwd").val('');
|
||||
});
|
||||
|
||||
/**
|
||||
* On send command button click
|
||||
*/
|
||||
$("#sendCommandButton").click(function() {
|
||||
connectionManager.sendConsoleCmd($("#commandInput").val());
|
||||
$("#commandInput").val('');
|
||||
commandHistoryIndex = -1; //Reset command history index
|
||||
});
|
||||
|
||||
/**
|
||||
* Enter or arrow down/up key on command input
|
||||
*/
|
||||
$("#commandInput").on('keyup', function (e) {
|
||||
if(e.which === 13){ //Detect enter key
|
||||
//Disable textbox to prevent multiple submit
|
||||
$(this).attr("disabled", "disabled");
|
||||
|
||||
//Send command
|
||||
sendCommandButton.click();
|
||||
|
||||
//Enable the textbox again.
|
||||
$(this).removeAttr("disabled");
|
||||
|
||||
//Focus again
|
||||
$(this).focus();
|
||||
}else if(e.which === 38){ //Detect arrow up key
|
||||
//Replace with older command
|
||||
if(commandHistoryIndex == -1){
|
||||
//If not browsing history, start by latest command sent
|
||||
commandHistoryIndex = connectionManager.activeConnection.commands.length;
|
||||
}
|
||||
$("#commandInput").val(connectionManager.activeConnection.commands[commandHistoryIndex - 1]);
|
||||
commandHistoryIndex = commandHistoryIndex - 1;
|
||||
}else if(e.which === 40){ //Detect arrow down key
|
||||
//Replace with newer command
|
||||
if(commandHistoryIndex !== -1){
|
||||
//If not browsing history, do nothing
|
||||
$("#commandInput").val(connectionManager.activeConnection.commands[commandHistoryIndex + 1]);
|
||||
commandHistoryIndex = commandHistoryIndex + 1;
|
||||
}
|
||||
}else if(e.which == 9){ //Detect tab key
|
||||
//TODO Suggest user from connectionManager.activeConnection.players;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* On delete server button click
|
||||
*/
|
||||
$("#deleteServerButton").click(function() {
|
||||
var name = connectionManager.activeConnection.serverName;
|
||||
//Remove subscribers
|
||||
connectionManager.activeConnection.removeSubscribers();
|
||||
|
||||
//Delete from active connections
|
||||
connectionManager.deleteConnection(name);
|
||||
|
||||
//Back to homepage
|
||||
backToHomepage();
|
||||
|
||||
//Remove from persistence
|
||||
persistenceManager.deleteServer(name);
|
||||
|
||||
//Update dropdown
|
||||
updateServerList();
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* On Navbar Home link clicked
|
||||
*/
|
||||
$("#navbarBrandLink").click(function() {
|
||||
backToHomepage();
|
||||
});
|
||||
|
||||
/**
|
||||
* On Navbar Brand link clicked
|
||||
*/
|
||||
$("#navbarHomeLink").click(function() {
|
||||
backToHomepage();
|
||||
});
|
||||
|
||||
/**
|
||||
* On DisconnectedModal, back to welcome screen clicked
|
||||
*/
|
||||
$("#disconnectionModalWelcomeScreenButton").click(function() {
|
||||
backToHomepage();
|
||||
});
|
||||
|
||||
/**
|
||||
* On Settings link clicked
|
||||
*/
|
||||
$("#settingsLink").click(function() {
|
||||
//Update modal switches and boxes with saved settings
|
||||
$("#showDateSettingsSwitch").prop("checked", persistenceManager.getSetting("dateTimePrefix"));
|
||||
$("#readLogFileSwitch").prop("checked", persistenceManager.getSetting("retrieveLogFile"));
|
||||
});
|
||||
|
||||
/**
|
||||
* On showDateSettingsSwitch switched
|
||||
*/
|
||||
$("#showDateSettingsSwitch").click(function() {
|
||||
//Update modal switches and boxes with saved settings
|
||||
persistenceManager.setSetting("dateTimePrefix", $("#showDateSettingsSwitch").is(":checked"));
|
||||
});
|
||||
|
||||
/**
|
||||
* On readLogFileSwitch switched
|
||||
*/
|
||||
$("#readLogFileSwitch").click(function() {
|
||||
//Update modal switches and boxes with saved settings
|
||||
persistenceManager.setSetting("retrieveLogFile", $("#readLogFileSwitch").is(":checked"));
|
||||
});
|
@ -1,599 +0,0 @@
|
||||
/**
|
||||
WebConsole Language Manager for WebConsole
|
||||
Used to save your preferred language into your browser
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
function setLanguage(locale){
|
||||
//Save to persistence
|
||||
persistenceManager.setLanguage(locale);
|
||||
//Set locale phrases
|
||||
switch(locale){
|
||||
case "en_US":
|
||||
lang = {
|
||||
"navbarHomeLink": "Home",
|
||||
"home_header": "Select a server from the menu",
|
||||
"home_description": "Use the navigation bar to add a new Minecraft Server or connect to a previously added one.",
|
||||
"serversDropdown": "Your servers",
|
||||
"add_server": "Add Server",
|
||||
"noServersAdded": "No servers added",
|
||||
"lang_dropdown": "Language",
|
||||
"addServerModalLongTitle": "Add a new server",
|
||||
"addServerModalSvName": "Server name:",
|
||||
"addServerModalSvIp": "Server IP:",
|
||||
"addServerModalSvPort": "WebConsole port:",
|
||||
"addServerModalSvSsl": "SSL is enabled on the server",
|
||||
"addServerModalSslAdvice": "SSL is required for connections made from HTTPS websites",
|
||||
"addServerModalClose": "Close",
|
||||
"saveAndConnectServerButton": "Save and connect",
|
||||
"passwordModalLongTitle": "Password required",
|
||||
"passwordModalLabel": "Password:",
|
||||
"passwordModalRememberLabel": "Remember password",
|
||||
"passwordModalCloseButton": "Close",
|
||||
"passwordSendButton": "Login",
|
||||
"disconnectionModalLongTitle": "Disconnected",
|
||||
"disconnectionModalDescription": "Connection was lost with the server. This can be caused by:",
|
||||
"disconnectionModalsub1": "Server was closed intentionally.",
|
||||
"disconnectionModalsub2": "Port is not opened on your host. In this case, troubleshoot using a port checker and recheck your firewall or router.",
|
||||
"disconnectionModalCloseButton": "Close",
|
||||
"disconnectionModalWelcomeScreenButton": "Back to welcome page",
|
||||
"settingsLink": "Settings",
|
||||
"settingsModalLongTitle": "WebConsole Settings",
|
||||
"showDateSettingsSwitchLabel": "Show time on each console line",
|
||||
"readLogFileSwitchLabel": "Retrieve full log file from server after login",
|
||||
"settingsModalCloseButton": "Done",
|
||||
"players_online": "Players Online",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM usage",
|
||||
"user_title": "Logged as",
|
||||
"deleteServerButton": "Delete server",
|
||||
"sendCommandButton": "Send"
|
||||
}
|
||||
break;
|
||||
case "es_ES":
|
||||
lang = {
|
||||
"navbarHomeLink": "Inicio",
|
||||
"home_header": "Selecciona un servidor del menú",
|
||||
"home_description": "Usa la barra superior para añadir un nuevo servidor de Minecraft o para conectarte a un servidor añadido previamente.",
|
||||
"serversDropdown": "Tus servidores",
|
||||
"add_server": "Añadir Server",
|
||||
"noServersAdded": "Ningun servidor guardado",
|
||||
"lang_dropdown": "Idioma",
|
||||
"addServerModalLongTitle": "Añadir un nuevo servidor",
|
||||
"addServerModalSvName": "Nombre del servidor:",
|
||||
"addServerModalSvIp": "IP del servidor:",
|
||||
"addServerModalSvPort": "Puerto WebConsole:",
|
||||
"addServerModalSvSsl": "SSL está activado",
|
||||
"addServerModalSslAdvice": "Te estás conectando al cliente por HTTPS, por tanto SSL es obligatorio",
|
||||
"addServerModalClose": "Cerrar",
|
||||
"saveAndConnectServerButton": "Guardar y conectar",
|
||||
"passwordModalLongTitle": "Se necesita contraseña",
|
||||
"passwordModalLabel": "Contraseña:",
|
||||
"passwordModalRememberLabel": "Recordar contraseña",
|
||||
"passwordModalCloseButton": "Cerrar",
|
||||
"passwordSendButton": "Iniciar sesión",
|
||||
"disconnectionModalLongTitle": "Desconectado",
|
||||
"disconnectionModalDescription": "Se perdió la conexión con el servidor. Esto puede deberse a:",
|
||||
"disconnectionModalsub1": "El servidor se cerró intencionadamente.",
|
||||
"disconnectionModalsub2": "El puerto no está abierto en el host. Utiliza un port checker para verificar que el puerto está abierto y comprueba tu firewall o router.",
|
||||
"disconnectionModalCloseButton": "Cerrar",
|
||||
"disconnectionModalWelcomeScreenButton": "Volver a pagina de inicio",
|
||||
"settingsLink": "Configuración",
|
||||
"settingsModalLongTitle": "Configuración de WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Mostrar hora en cada linea de consola",
|
||||
"readLogFileSwitchLabel": "Leer log completo al iniciar sesión",
|
||||
"settingsModalCloseButton": "Hecho",
|
||||
"players_online": "Jugadores en línea",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM en uso",
|
||||
"user_title": "Iniciado sesion como",
|
||||
"deleteServerButton": "Borrar servidor",
|
||||
"sendCommandButton": "Enviar"
|
||||
}
|
||||
break;
|
||||
case "ru_RU": //Credit to Stashenko
|
||||
lang = {
|
||||
"navbarHomeLink": "Главная",
|
||||
"home_header": "Выберите сервер из меню",
|
||||
"home_description": "Используйте панель навигации, чтобы добавить новый сервер Minecraft или подключиться к ранее добавленному.",
|
||||
"serversDropdown": "Ваши серверы",
|
||||
"add_server": "Добавить сервер",
|
||||
"noServersAdded": "Серверы не добавлены",
|
||||
"lang_dropdown": "Язык",
|
||||
"addServerModalLongTitle": "Добавить новый сервер",
|
||||
"addServerModalSvName": "Название сервера:",
|
||||
"addServerModalSvIp": "IP сервера:",
|
||||
"addServerModalSvPort": "Порт WebConsole:",
|
||||
"addServerModalSvSsl": "Сервер с поддержкой SSL",
|
||||
"addServerModalSslAdvice": "SSL требуется для клиентских подключений HTTPS",
|
||||
"addServerModalClose": "Закрыть",
|
||||
"saveAndConnectServerButton": "Сохранить и подключить",
|
||||
"passwordModalLongTitle": "Требуется пароль",
|
||||
"passwordModalLabel": "Пароль:",
|
||||
"passwordModalRememberLabel": "Запомнить пароль",
|
||||
"passwordModalCloseButton": "Закрыть",
|
||||
"passwordSendButton": "Войти",
|
||||
"disconnectionModalLongTitle": "Отключение!",
|
||||
"disconnectionModalDescription": "Было потеряно соединение с сервером. Это может быть вызвано:",
|
||||
"disconnectionModalsub1": "Сервер был закрыт намеренно.",
|
||||
"disconnectionModalsub2": "Порт не открыт на вашем хосте. В этом случае устраните неполадки с помощью средства проверки портов и еще раз проверьте свой брандмауэр или маршрутизатор.",
|
||||
"disconnectionModalCloseButton": "Закрыть",
|
||||
"disconnectionModalWelcomeScreenButton": "Вернуться на страницу приветствия",
|
||||
"settingsLink": "настройки",
|
||||
"settingsModalLongTitle": "настройки WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Показать время в каждой строке консоли",
|
||||
"readLogFileSwitchLabel": "Получить полный файл журнала с сервера после входа в систему",
|
||||
"settingsModalCloseButton": "Выполнено",
|
||||
"players_online": "Игроки",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM",
|
||||
"user_title": "Вы вошли как",
|
||||
"deleteServerButton": "Удалить сервер",
|
||||
"sendCommandButton": "Отправить"
|
||||
}
|
||||
break;
|
||||
case "pt_BR": //Credit to AlexandreMuassab
|
||||
lang = {
|
||||
"navbarHomeLink": "Principal",
|
||||
"home_header": "Selecione o seu servidor na aba acima.",
|
||||
"home_description": "Use a barra de navegação para adicionar ou conectar-se entre os servidores.",
|
||||
"serversDropdown": "Servidores",
|
||||
"add_server": "Adicionar um servidor",
|
||||
"noServersAdded": "Nenhum servidor adicionado",
|
||||
"lang_dropdown": "Idioma",
|
||||
"addServerModalLongTitle": "Adicionar um novo servidor",
|
||||
"addServerModalSvName": "Nome:",
|
||||
"addServerModalSvIp": "IP:",
|
||||
"addServerModalSvPort": "Porta(config.yml):",
|
||||
"addServerModalSvSsl": "SSL do servidor está habilitado ?",
|
||||
"addServerModalSslAdvice": "É necessário SSL para conexões do cliente HTTPS",
|
||||
"addServerModalClose": "Fechar",
|
||||
"saveAndConnectServerButton": "Salvar e conectar",
|
||||
"passwordModalLongTitle": "Necessário uma senha",
|
||||
"passwordModalLabel": "Senha:",
|
||||
"passwordModalRememberLabel": "Deseja salvar esta senha ?",
|
||||
"passwordModalCloseButton": "Fechar",
|
||||
"passwordSendButton": "Logar",
|
||||
"disconnectionModalLongTitle": "desconectado",
|
||||
"disconnectionModalDescription": "A conexão com o servidor foi perdida. Isso pode ser causado por:",
|
||||
"disconnectionModalsub1": "O servidor foi fechado intencionalmente.",
|
||||
"disconnectionModalsub2": "A porta não está aberta em seu host. Se for esse o caso, solucione o problema com um testador de porta e verifique seu firewall ou router novamente.",
|
||||
"disconnectionModalCloseButton": "Fechar",
|
||||
"disconnectionModalWelcomeScreenButton": "Voltar à página de boas-vindas",
|
||||
"settingsLink": "Configurações",
|
||||
"settingsModalLongTitle": "Configurações do WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Mostrar hora em cada linha do console",
|
||||
"readLogFileSwitchLabel": "Recuperar arquivo de log completo do servidor após o login",
|
||||
"settingsModalCloseButton": "Feito",
|
||||
"players_online": "Jogadores online",
|
||||
"cpu_title": "Consumo de CPU",
|
||||
"ram_title": "Consumo de RAM",
|
||||
"user_title": "Logado como",
|
||||
"deleteServerButton": "Remover este servidor",
|
||||
"sendCommandButton": "Enviar"
|
||||
}
|
||||
break;
|
||||
case "zh_CN": //Credit to Neubulae
|
||||
lang = {
|
||||
"navbarHomeLink": "首页",
|
||||
"home_header": "请从菜单中选择一个服务器",
|
||||
"home_description": "请使用导航栏以添加新服务器, 或连接至已设定服务器.",
|
||||
"serversDropdown": "你的服务器",
|
||||
"add_server": "添加服务器",
|
||||
"noServersAdded": "没有添加服务器",
|
||||
"lang_dropdown": "语言",
|
||||
"addServerModalLongTitle": "添加一个新服务器",
|
||||
"addServerModalSvName": "服务器名称:",
|
||||
"addServerModalSvIp": "服务器IP地址:",
|
||||
"addServerModalSvPort": "WebConsole端口:",
|
||||
"addServerModalSvSsl": "服务器已开启SSL",
|
||||
"addServerModalSslAdvice": "HTTPS客户端连接需要SSL",
|
||||
"addServerModalClose": "关闭",
|
||||
"saveAndConnectServerButton": "保存并连接",
|
||||
"passwordModalLongTitle": "需要密码",
|
||||
"passwordModalLabel": "密码:",
|
||||
"passwordModalRememberLabel": "记住密码",
|
||||
"passwordModalCloseButton": "关闭",
|
||||
"passwordSendButton": "登录",
|
||||
"disconnectionModalLongTitle": "已断开",
|
||||
"disconnectionModalDescription": "与服务器的连接丢失。 这可能是由于:",
|
||||
"disconnectionModalsub1": "服务器是有意关闭的。",
|
||||
"disconnectionModalsub2": "您的主机上的端口未打开。 如果是这种情况,请使用端口测试仪进行故障排除,然后再次检查防火墙或路由器。",
|
||||
"disconnectionModalCloseButton": "关闭",
|
||||
"disconnectionModalWelcomeScreenButton": "返回欢迎页面",
|
||||
"settingsLink": "设定值",
|
||||
"settingsModalLongTitle": "WebConsole 设定值",
|
||||
"showDateSettingsSwitchLabel": "在每个控制台行上显示时间",
|
||||
"readLogFileSwitchLabel": "登录后从服务器检索完整的日志文件",
|
||||
"settingsModalCloseButton": "完成",
|
||||
"players_online": "在线人数",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "内存使用量",
|
||||
"user_title": "登录为",
|
||||
"deleteServerButton": "关闭服务器",
|
||||
"sendCommandButton": "发送"
|
||||
}
|
||||
break;
|
||||
case "ko_KR":
|
||||
lang = {
|
||||
"navbarHomeLink": "메인",
|
||||
"home_header": "메뉴에서 서버를 선택해 주세요",
|
||||
"home_description": "새 서버를 추가하거나 전에 추가한 서버에 연결하려면 네비게이션 바를 사용하세요.",
|
||||
"serversDropdown": "서버 목록",
|
||||
"add_server": "서버 추가하기",
|
||||
"noServersAdded": "서버 없음",
|
||||
"lang_dropdown": "언어",
|
||||
"addServerModalLongTitle": "서버 추가",
|
||||
"addServerModalSvName": "서버 이름:",
|
||||
"addServerModalSvIp": "서버 주소:",
|
||||
"addServerModalSvPort": "WebConsole 포트:",
|
||||
"addServerModalSvSsl": "서버가 SSL을 사용합니다",
|
||||
"addServerModalSslAdvice": "SSL은 HTTPS 연결을 위해 필요합니다",
|
||||
"addServerModalClose": "닫기",
|
||||
"saveAndConnectServerButton": "저장하고 연결하기",
|
||||
"passwordModalLongTitle": "비밀번호 필요",
|
||||
"passwordModalLabel": "비밀번호:",
|
||||
"passwordModalRememberLabel": "비밀번호 기억",
|
||||
"passwordModalCloseButton": "닫기",
|
||||
"passwordSendButton": "로그인",
|
||||
"disconnectionModalLongTitle": "연결 끊김",
|
||||
"disconnectionModalDescription": "서버와의 연결이 끊어졌습니다. 원인은 다음과 같습니다.",
|
||||
"disconnectionModalsub1": "서버가 의도적으로 닫혔습니다.",
|
||||
"disconnectionModalsub2": "호스트에서 포트가 열려 있지 않습니다. 이 경우 포트 테스터로 문제를 해결하고 방화벽이나 라우터를 다시 확인하십시오.",
|
||||
"disconnectionModalCloseButton": "닫기",
|
||||
"disconnectionModalWelcomeScreenButton": "메인으로 돌아가기",
|
||||
"settingsLink": "설정",
|
||||
"settingsModalLongTitle": "WebConsole 설정",
|
||||
"showDateSettingsSwitchLabel": "모든 줄에 시각 표시하기",
|
||||
"readLogFileSwitchLabel": "로그인 후 전체 로그 파일 읽기",
|
||||
"settingsModalCloseButton": "완료",
|
||||
"players_online": "온라인인 플레이어",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM 사용량",
|
||||
"user_title": "로그인된 사용자:",
|
||||
"deleteServerButton": "서버 삭제하기",
|
||||
"sendCommandButton": "전송"
|
||||
}
|
||||
break;
|
||||
case "fr_FR":
|
||||
lang = {
|
||||
"navbarHomeLink": "Page d'accueil",
|
||||
"home_header": "Sélectionnez un serveur dans le menu",
|
||||
"home_description": "Utilisez la barre de navigation pour ajouter un nouveau serveur ou connectez-vous à un autre précédemment ajouté.",
|
||||
"serversDropdown": "Vos serveurs",
|
||||
"add_server": "Ajouter un serveur",
|
||||
"noServersAdded": "Aucun serveur ajouté",
|
||||
"lang_dropdown": "Langue",
|
||||
"addServerModalLongTitle": "Ajouter un nouveau serveur",
|
||||
"addServerModalSvName": "Nom du serveur:",
|
||||
"addServerModalSvIp": "Serveur IP:",
|
||||
"addServerModalSvPort": "WebConsole port:",
|
||||
"addServerModalSvSsl": "SSL est activé",
|
||||
"addServerModalSslAdvice": "SSL est requis si vous vous connectez à partir d'un client HTTPS",
|
||||
"addServerModalClose": "Fermer",
|
||||
"saveAndConnectServerButton": "Enregistrez et connectez",
|
||||
"passwordModalLongTitle": "Mot de passe requis",
|
||||
"passwordModalLabel": "Mot de passe:",
|
||||
"passwordModalRememberLabel": "Se souvenir du mot de passe",
|
||||
"passwordModalCloseButton": "Fermer",
|
||||
"passwordSendButton": "S'identifier",
|
||||
"disconnectionModalLongTitle": "Débranché",
|
||||
"disconnectionModalDescription": "La connexion avec le serveur a été perdue. Cela peut être causé par:",
|
||||
"disconnectionModalsub1": "Le serveur a été fermé intentionnellement.",
|
||||
"disconnectionModalsub2": "Le port n'est pas ouvert sur votre hôte. Si tel est le cas, dépannez avec un testeur de port et vérifiez à nouveau votre firewall ou votre router.",
|
||||
"disconnectionModalCloseButton": "Fermer",
|
||||
"disconnectionModalWelcomeScreenButton": "Retour à la page d'accueil",
|
||||
"settingsLink": "Réglages",
|
||||
"settingsModalLongTitle": "Réglages de WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Afficher l'heure sur chaque ligne de console",
|
||||
"readLogFileSwitchLabel": "Récupérer le fichier journal complet du serveur après la connexion",
|
||||
"settingsModalCloseButton": "Terminé",
|
||||
"players_online": "Joueurs en ligne",
|
||||
"cpu_title": "Utilisation de la CPU",
|
||||
"ram_title": "Utilisation de la RAM",
|
||||
"user_title": "Connecté en tant que",
|
||||
"deleteServerButton": "Supprimer le serveur",
|
||||
"sendCommandButton": "Envoyer"
|
||||
}
|
||||
break;
|
||||
case "cs_CZ":
|
||||
lang = {
|
||||
"navbarHomeLink": "Domů",
|
||||
"home_header": "Vyberte server z nabídky",
|
||||
"home_description": "Použijte navigační lištu pro přidání nového minecraftového serveru, nebo pro připojení k existujícímu.",
|
||||
"serversDropdown": "Vaše servery",
|
||||
"add_server": "Přidat server",
|
||||
"noServersAdded": "Nebyly přidány žádné servery",
|
||||
"lang_dropdown": "Jazyk",
|
||||
"addServerModalLongTitle": "Přidat nový server",
|
||||
"addServerModalSvName": "Jméno serveru:",
|
||||
"addServerModalSvIp": "IP adresa serveru:",
|
||||
"addServerModalSvPort": "Port WebConsole:",
|
||||
"addServerModalSvSsl": "SSL je zapnuté na tomto serveru",
|
||||
"addServerModalSslAdvice": "SSL je vyžadováno pro připojení klientů pomocí HTTPS",
|
||||
"addServerModalClose": "Zavřít",
|
||||
"saveAndConnectServerButton": "Uložit a připojit",
|
||||
"passwordModalLongTitle": "Vyžadováno heslo",
|
||||
"passwordModalLabel": "Heslo:",
|
||||
"passwordModalRememberLabel": "Pamatovat si heslo",
|
||||
"passwordModalCloseButton": "Zavřít",
|
||||
"passwordSendButton": "Přihlásit se",
|
||||
"disconnectionModalLongTitle": "Odpojeno",
|
||||
"disconnectionModalDescription": "Připojení bylo ztraceno se serverem. To může být způsobeno:",
|
||||
"disconnectionModalsub1": "Server byl úmyslně uzavřen.",
|
||||
"disconnectionModalsub2": "Port není na hostiteli otevřený. Pokud tomu tak je, odstraňte problém s testerem portů a znovu zkontrolujte firewall nebo router.",
|
||||
"disconnectionModalCloseButton": "Zavřít",
|
||||
"disconnectionModalWelcomeScreenButton": "Zpět na úvodní stránku",
|
||||
"settingsLink": "Nastavení",
|
||||
"settingsModalLongTitle": "Nastavení WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Zobrazit čas v každém řádku konzoly",
|
||||
"readLogFileSwitchLabel": "Po přihlášení načtěte úplný soubor protokolu ze serveru",
|
||||
"settingsModalCloseButton": "Hotovo",
|
||||
"players_online": "Počet hráčů online",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "Využití RAM",
|
||||
"user_title": "Přihlášen jako",
|
||||
"deleteServerButton": "Odstranit server",
|
||||
"sendCommandButton": "Odeslat"
|
||||
}
|
||||
break;
|
||||
case "it_IT": //Credit to AlexZap
|
||||
lang = {
|
||||
"navbarHomeLink": "Home",
|
||||
"home_header": "Seleziona un server dal menù",
|
||||
"home_description": "Usa la barra di navigazione per connetterti al server o clicca sul menù a tendina sovrastante.",
|
||||
"serversDropdown": "I tuoi server",
|
||||
"add_server": "Aggiungi un Server",
|
||||
"noServersAdded": "Nessun server aggiunto",
|
||||
"lang_dropdown": "Lingua",
|
||||
"addServerModalLongTitle": "Aaggiungi un nuovo server",
|
||||
"addServerModalSvName": "Nome server:",
|
||||
"addServerModalSvIp": "IP server:",
|
||||
"addServerModalSvPort": "Porta WebConsole:",
|
||||
"addServerModalSvSsl": "Il server è abilitato a SSL",
|
||||
"addServerModalSslAdvice": "L'SSL e richiesto in caso di connessioni HTTPS",
|
||||
"addServerModalClose": "Chiudi",
|
||||
"saveAndConnectServerButton": "Salva e connettiti",
|
||||
"passwordModalLongTitle": "Password richiesta",
|
||||
"passwordModalLabel": "Password:",
|
||||
"passwordModalRememberLabel": "Ricorda la mia password",
|
||||
"passwordModalCloseButton": "Chiudi",
|
||||
"passwordSendButton": "Login",
|
||||
"disconnectionModalLongTitle": "Disconnesso",
|
||||
"disconnectionModalDescription": "La connessione con il server è stata persa. Ciò può essere causato da:",
|
||||
"disconnectionModalsub1": "Il server è stato chiuso intenzionalmente.",
|
||||
"disconnectionModalsub2": "La porta non è aperta sul tuo host. In tal caso, risolvere i problemi con un tester della porta e controllare nuovamente il firewall o il router.",
|
||||
"disconnectionModalCloseButton": "Chiudi",
|
||||
"disconnectionModalWelcomeScreenButton": "Torna alla Home",
|
||||
"settingsLink": "Impostazioni",
|
||||
"settingsModalLongTitle": "Impostazioni WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Mostra la data e l'ora per ogni linea della console",
|
||||
"readLogFileSwitchLabel": "Mostra l'intero file log dopo il login",
|
||||
"settingsModalCloseButton": "Conferma",
|
||||
"players_online": "Giocatori online",
|
||||
"cpu_title": "Utilizzo CPU",
|
||||
"ram_title": "Utilizzo RAM",
|
||||
"user_title": "Collegato come",
|
||||
"deleteServerButton": "Cancella il server",
|
||||
"sendCommandButton": "Invio"
|
||||
}
|
||||
break;
|
||||
case "nl_NL": //Credit to Twockx
|
||||
lang = {
|
||||
"navbarHomeLink": "Homepagina",
|
||||
"home_header": "Selecteer een server van het menu",
|
||||
"home_description": "Gebruik de navigatiebalk om een nieuwe Minecraft-server toe te voegen of maak verbinding met een eerder toegevoegde server.",
|
||||
"serversDropdown": "Uw servers",
|
||||
"add_server": "Voeg server toe",
|
||||
"noServersAdded": "Geen servers toegevoegd",
|
||||
"lang_dropdown": "Taal",
|
||||
"addServerModalLongTitle": "Voeg een nieuwe server toe",
|
||||
"addServerModalSvName": "Server naam:",
|
||||
"addServerModalSvIp": "Server IP:",
|
||||
"addServerModalSvPort": "WebConsole poort:",
|
||||
"addServerModalSvSsl": "Server SSL is ingeschakeld",
|
||||
"addServerModalSslAdvice": "SSL is vereist voor HTTPS-clientverbindingen",
|
||||
"addServerModalClose": "Sluiten",
|
||||
"saveAndConnectServerButton": "Opslaan en verbinden",
|
||||
"passwordModalLongTitle": "Wachtwoord vereist",
|
||||
"passwordModalLabel": "Wachtwoord:",
|
||||
"passwordModalRememberLabel": "Onthoud wachtwoord",
|
||||
"passwordModalCloseButton": "Sluiten",
|
||||
"passwordSendButton": "Log in",
|
||||
"disconnectionModalLongTitle": "Verbinding verbroken",
|
||||
"disconnectionModalDescription": "De verbinding met de server is verbroken. Dit kan worden veroorzaakt door:",
|
||||
"disconnectionModalsub1": "De server is opzettelijk gesloten.",
|
||||
"disconnectionModalsub2": "De poort is niet open op uw host. Als dit het geval is, lost u het probleem op met een poorttester en controleert u uw firewall of router opnieuw.",
|
||||
"disconnectionModalCloseButton": "Sluiten",
|
||||
"disconnectionModalWelcomeScreenButton": "Terug naar homepagina",
|
||||
"settingsLink": "Instellingen",
|
||||
"settingsModalLongTitle": "WebConsole Instellingen",
|
||||
"showDateSettingsSwitchLabel": "Toon tijd op elke consolelijn",
|
||||
"readLogFileSwitchLabel": "Haal het volledige logbestand op van de server na inloggen",
|
||||
"settingsModalCloseButton": "Klaar",
|
||||
"players_online": "Spelers online",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM gebruik",
|
||||
"user_title": "Aangemeld als",
|
||||
"deleteServerButton": "Verwijder server",
|
||||
"sendCommandButton": "Stuur"
|
||||
}
|
||||
break;
|
||||
case "de_DE": //Credit to NoNamePro0
|
||||
lang = {
|
||||
"navbarHomeLink": "Startseite",
|
||||
"home_header": "Willkommen bei der WebConsole",
|
||||
"home_description": "Nutze die Menübar um einen Server beizutreten",
|
||||
"serversDropdown": "Deine Server",
|
||||
"add_server": "Neuen Server hinzufügen",
|
||||
"noServersAdded": "Keine Server hinzugefügt",
|
||||
"lang_dropdown": "Sprache",
|
||||
"addServerModalLongTitle": "Hinzufüge deinen Server",
|
||||
"addServerModalSvName": "Server-Name:",
|
||||
"addServerModalSvIp": "Server-IP:",
|
||||
"addServerModalSvPort": "WebConsole Port:",
|
||||
"addServerModalSvSsl": "Server ist SSL verschlüsselt",
|
||||
"addServerModalSslAdvice": "SSL ist erforderlich für HTTPS Verbindungen",
|
||||
"addServerModalClose": "Schließen",
|
||||
"saveAndConnectServerButton": "Speichern und verbinden",
|
||||
"passwordModalLongTitle": "Passwort benötigt",
|
||||
"passwordModalLabel": "Passwort:",
|
||||
"passwordModalRememberLabel": "Passwort merken",
|
||||
"passwordModalCloseButton": "Schließen",
|
||||
"passwordSendButton": "Login",
|
||||
"disconnectionModalLongTitle": "Verbindung getrennt",
|
||||
"disconnectionModalDescription": "Die Verbindung zum Server wurde unterbrochen. Dies kann verursacht werden durch:",
|
||||
"disconnectionModalsub1": "Server wurde absichtlich geschlossen.",
|
||||
"disconnectionModalsub2": "Der Port ist auf Ihrem Host nicht geöffnet. Wenn dies der Fall ist, beheben Sie die Fehlerbehebung mit einem Port-Tester und überprüfen Sie Ihre Firewall oder Ihren Router erneut.",
|
||||
"disconnectionModalCloseButton": "Schließen",
|
||||
"disconnectionModalWelcomeScreenButton": "Zurück zur Startseite",
|
||||
"settingsLink": "Einstellungen",
|
||||
"settingsModalLongTitle": "WebConsole Einstellungen",
|
||||
"showDateSettingsSwitchLabel": "Zeig die Uhrzeit vor jeder Linie in der Console",
|
||||
"readLogFileSwitchLabel": "Zeige die komplette Log nach dem Login",
|
||||
"settingsModalCloseButton": "Fertig!",
|
||||
"players_online": "Spieler Online",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM",
|
||||
"user_title": "Angemeldet als",
|
||||
"deleteServerButton": "Server entfernen",
|
||||
"sendCommandButton": "Senden"
|
||||
}
|
||||
break;
|
||||
case "tr_TR": //Credit to acarnd03
|
||||
lang = {
|
||||
"navbarHomeLink": "Ev",
|
||||
"home_header": "Menüden bir sunucu seçin",
|
||||
"home_description": "Yeni bir Minecraft Sunucusu eklemek veya önceden eklenmiş bir sunucuya bağlanmak için gezinme çubuğunu kullanın.",
|
||||
"serversDropdown": "Sunucularınız",
|
||||
"add_server": "Sunucu ekle",
|
||||
"noServersAdded": "Sunucu eklenmedi",
|
||||
"lang_dropdown": "Dil",
|
||||
"addServerModalLongTitle": "Yeni bir sunucu ekleyin",
|
||||
"addServerModalSvName": "Sunucu adı:",
|
||||
"addServerModalSvIp": "Sunucu IP'si:",
|
||||
"addServerModalSvPort": "WebConsole bağlantı noktası:",
|
||||
"addServerModalSvSsl": "Sunucu SSL etkin",
|
||||
"addServerModalSslAdvice": "HTTPS istemci bağlantıları için SSL gereklidir",
|
||||
"addServerModalClose": "Kapat",
|
||||
"saveAndConnectServerButton": "Kaydet ve bağlan",
|
||||
"passwordModalLongTitle": "Şifre gereklidir",
|
||||
"passwordModalLabel": "Parola:",
|
||||
"passwordModalRememberLabel": "Şifremi hatırla",
|
||||
"passwordModalCloseButton": "Kapat",
|
||||
"passwordSendButton": "Oturum aç",
|
||||
"disconnectionModalLongTitle": "Bağlantı kesildi",
|
||||
"disconnectionModalDescription": "Sunucuyla bağlantı kesildi. Bunun nedeni şunlar olabilir:",
|
||||
"disconnectionModalsub1": "Sunucu kasıtlı olarak kapatıldı.",
|
||||
"disconnectionModalsub2": "Ana makinenizde bağlantı noktası açılmadı. Bu durumda, bir bağlantı noktası denetleyicisi kullanarak sorunu giderin ve güvenlik duvarınızı veya yönlendiricinizi yeniden kontrol edin.",
|
||||
"disconnectionModalCloseButton": "Kapat",
|
||||
"disconnectionModalWelcomeScreenButton": "Karşılama sayfasına geri dön",
|
||||
"settingsLink": "Ayarlar",
|
||||
"settingsModalLongTitle": "WebConsole Ayarları",
|
||||
"showDateSettingsSwitchLabel": "Her konsol satırında zamanı göster",
|
||||
"readLogFileSwitchLabel": "Giriş yaptıktan sonra tam günlük dosyasını sunucudan alın",
|
||||
"settingsModalCloseButton": "Bitti",
|
||||
"players_online": "Çevrimiçi Oyuncular",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM kullanımı",
|
||||
"user_title": "olarak giriş yapıldı",
|
||||
"deleteServerButton": "Sunucuyu silin",
|
||||
"sendCommandButton": "Gönder"
|
||||
}
|
||||
break;
|
||||
case "ja_JA": //Credit to kuroneko6423 | https://kuroneko6423.com
|
||||
lang = {
|
||||
"navbarHomeLink": "ホーム",
|
||||
"home_header": "メニューからサーバーを選択",
|
||||
"home_description": "ナビゲーションバーを使って、新しいMinecraftサーバーを追加したり、以前に追加したサーバーに接続したりします。",
|
||||
"serversDropdown": "あなたのサーバー",
|
||||
"add_server": "サーバー追加",
|
||||
"noServersAdded": "追加されたサーバーはありません",
|
||||
"lang_dropdown": "言語",
|
||||
"addServerModalLongTitle": "新規サーバーの追加",
|
||||
"addServerModalSvName": "サーバー名:",
|
||||
"addServerModalSvIp": "サーバーIP:",
|
||||
"addServerModalSvPort": "WebConsoleポート:",
|
||||
"addServerModalSvSsl": "サーバーでSSLが有効になっている",
|
||||
"addServerModalSslAdvice": "HTTPSウェブサイトからの接続にはSSLが必要です。",
|
||||
"addServerModalClose": "閉じる",
|
||||
"saveAndConnectServerButton": "保存と接続",
|
||||
"passwordModalLongTitle": "パスワードが必要です",
|
||||
"passwordModalLabel": "パスワード:",
|
||||
"passwordModalRememberLabel": "パスワードの記憶",
|
||||
"passwordModalCloseButton": "閉じる",
|
||||
"passwordSendButton": "ログイン",
|
||||
"disconnectionModalLongTitle": "切断",
|
||||
"disconnectionModalDescription": "サーバーとの接続が切断されました:",
|
||||
"disconnectionModalsub1": "サーバーが意図的に閉じられました。",
|
||||
"disconnectionModalsub2": "ポートがホスト上で開かれていません。この場合、ポートチェッカーを使ってトラブルシューティングを行い、ファイアウォールやルーターを再確認してください。",
|
||||
"disconnectionModalCloseButton": "閉じる",
|
||||
"disconnectionModalWelcomeScreenButton": "ホームに戻る",
|
||||
"settingsLink": "設定",
|
||||
"settingsModalLongTitle": "WebConsole 設定",
|
||||
"showDateSettingsSwitchLabel": "各コンソールラインに時間を表示",
|
||||
"readLogFileSwitchLabel": "ログイン後にサーバーからフルログファイルを取得する",
|
||||
"settingsModalCloseButton": "了承",
|
||||
"players_online": "プレイヤーオンライン",
|
||||
"cpu_title": "CPU",
|
||||
"ram_title": "RAM",
|
||||
"user_title": "ログインします。",
|
||||
"deleteServerButton": "サーバーを削除",
|
||||
"sendCommandButton": "送信"
|
||||
}
|
||||
break;
|
||||
case "pl_PL": //Credit to gpewojan1
|
||||
lang = {
|
||||
"navbarHomeLink": "Strona główna",
|
||||
"home_header": "Wybierz serwer z menu",
|
||||
"home_description": "Użyj zakładki \"Twoje serwery\", aby dodać nowy serwer Minecraft lub połączyć się do serwera dodanego wcześniej",
|
||||
"serversDropdown": "Twoje serwery",
|
||||
"add_server": "Dodaj Serwer",
|
||||
"noServersAdded": "Nie dodano żadnych serwerów",
|
||||
"lang_dropdown": "Język",
|
||||
"addServerModalLongTitle": "Dodaj nowy serwer",
|
||||
"addServerModalSvName": "Nazwa serwera:",
|
||||
"addServerModalSvIp": "IP Serwera:",
|
||||
"addServerModalSvPort": "Port WebConsole:",
|
||||
"addServerModalSvSsl": "SSL jest włączony na tym serwerze",
|
||||
"addServerModalSslAdvice": "SSL jest wymagany do połączeń ze stronami HTTPS",
|
||||
"addServerModalClose": "Zamknij",
|
||||
"saveAndConnectServerButton": "Zapisz i połącz",
|
||||
"passwordModalLongTitle": "Hasło jest wymagane",
|
||||
"passwordModalLabel": "Hasło:",
|
||||
"passwordModalRememberLabel": "Zapamiętaj hasło",
|
||||
"passwordModalCloseButton": "Zamknij",
|
||||
"passwordSendButton": "Zaloguj się",
|
||||
"disconnectionModalLongTitle": "Rozłączono",
|
||||
"disconnectionModalDescription": "Połączenie z serwerem zostało zerwane. Możliwe powody:",
|
||||
"disconnectionModalsub1": "Serwer został wyłączony intencjonalnie.",
|
||||
"disconnectionModalsub2": "Port nie jest otworzony przez host. W tym przypadku sprawdź, czy port jest otwarty używając port checkera i sprawdź twój firewall oraz router.",
|
||||
"disconnectionModalCloseButton": "Zamknij",
|
||||
"disconnectionModalWelcomeScreenButton": "Strona główna",
|
||||
"settingsLink": "Ustawienia",
|
||||
"settingsModalLongTitle": "Ustawienia WebConsole",
|
||||
"showDateSettingsSwitchLabel": "Pokaż czas na każdej linijce w konsoli",
|
||||
"readLogFileSwitchLabel": "Pokaż pełny log z serwera po zalogowaniu się",
|
||||
"settingsModalCloseButton": "Ok",
|
||||
"players_online": "Gracze Online",
|
||||
"cpu_title": "Zużycie CPU",
|
||||
"ram_title": "Zużycie RAM",
|
||||
"user_title": "Zalogowano jako",
|
||||
"deleteServerButton": "Usuń serwer",
|
||||
"sendCommandButton": "Wyślij"
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("No language set");
|
||||
}
|
||||
|
||||
//Set phrases
|
||||
jQuery.each(lang, (key, value) =>{
|
||||
try{
|
||||
document.getElementById(key).textContent = value;
|
||||
}catch(err){
|
||||
console.error("Cannot translate " + key + " (" + value + ")")
|
||||
}
|
||||
});
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/**
|
||||
WebConsole Manager for WebConsole
|
||||
Used to manage active connections
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
class WebConsoleManager {
|
||||
constructor(){
|
||||
this.activeConnections = []; //Active Connectors list
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a existing connection or creates a new one
|
||||
*/
|
||||
loadConnection(serverName){
|
||||
//If a connection is already active, delete all subscribers
|
||||
if(this.activeConnection){
|
||||
this.activeConnection.removeSubscribers();
|
||||
}
|
||||
|
||||
//If connection exists, load it
|
||||
var manager = this;
|
||||
var i;
|
||||
for (i = 0; i < this.activeConnections.length; i++) {
|
||||
if(this.activeConnections[i].serverName == serverName){
|
||||
manager.activeConnection = this.activeConnections[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If not created yet, create it
|
||||
var serverObj = new WebConsolePersistenceManager().getServer(serverName);
|
||||
this.activeConnection = new WebConsoleConnector(serverObj.serverName, serverObj.serverURI);
|
||||
this.activeConnection.connect();
|
||||
|
||||
//Save to connections list
|
||||
this.activeConnections.push(this.activeConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes connection (for example, if a connection was closed by server).
|
||||
* Called by WebConsole.js
|
||||
*/
|
||||
deleteConnection(serverName, deleteFromArrayOnly){
|
||||
//Delete from active connection (if it is the active one)
|
||||
if(!deleteFromArrayOnly && this.activeConnection.serverName == serverName){
|
||||
this.activeConnection = null;
|
||||
}
|
||||
|
||||
//Delete from array
|
||||
var i;
|
||||
for (i = 0; i < this.activeConnections.length; i++) {
|
||||
if(this.activeConnections[i].serverName == serverName){
|
||||
this.activeConnections.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send password to server
|
||||
*/
|
||||
sendPassword(pwd){
|
||||
this.activeConnection.sendToServer({
|
||||
command: "LOGIN",
|
||||
params: pwd
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send console command to server
|
||||
*/
|
||||
sendConsoleCmd(cmd){
|
||||
this.activeConnection.sendToServer({
|
||||
command: "EXEC",
|
||||
token: this.activeConnection.token,
|
||||
params: cmd
|
||||
});
|
||||
|
||||
this.activeConnection.commands.push(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks server for CPU, RAM and players info
|
||||
*/
|
||||
askForInfo(){
|
||||
this.activeConnection.sendToServer({
|
||||
command: "PLAYERS",
|
||||
token: this.activeConnection.token,
|
||||
});
|
||||
|
||||
this.activeConnection.sendToServer({
|
||||
command: "CPUUSAGE",
|
||||
token: this.activeConnection.token,
|
||||
});
|
||||
|
||||
this.activeConnection.sendToServer({
|
||||
command: "RAMUSAGE",
|
||||
token: this.activeConnection.token,
|
||||
});
|
||||
|
||||
this.activeConnection.sendToServer({
|
||||
command: "TPS",
|
||||
token: this.activeConnection.token,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks server for full latest.log
|
||||
*/
|
||||
askForLogs(){
|
||||
this.activeConnection.sendToServer({
|
||||
command: "READLOGFILE",
|
||||
token: this.activeConnection.token,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
/**
|
||||
WebConsole Persistence Manager for WebConsole
|
||||
Used to save your servers and config into your browser
|
||||
https://github.com/mesacarlos
|
||||
2019-2020 Carlos Mesa under MIT License.
|
||||
*/
|
||||
class WebConsolePersistenceManager{
|
||||
|
||||
/**
|
||||
* Saves or updates server into WebStorage
|
||||
*/
|
||||
saveServer(serverObject){
|
||||
//Check if server exists
|
||||
var i;
|
||||
var found = false;
|
||||
var servers = this.getAllServers();
|
||||
for (i = 0; i < servers.length; i++) {
|
||||
if(servers[i].serverName == serverObject.serverName){
|
||||
//Exists, replacing it
|
||||
servers[i] = serverObject;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Not found, adding it
|
||||
if(found == false){
|
||||
servers.push(serverObject);
|
||||
}
|
||||
|
||||
this.replaceAllServers(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete server from saved servers
|
||||
*/
|
||||
deleteServer(serverName){
|
||||
//Find server
|
||||
var i;
|
||||
var index = -1;
|
||||
var servers = this.getAllServers();
|
||||
for (i = 0; i < servers.length; i++) {
|
||||
if(servers[i].serverName == serverName){
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
//Delete it
|
||||
if(index > -1){
|
||||
servers.splice(index, 1);
|
||||
}
|
||||
|
||||
//Save to WebStorage
|
||||
this.replaceAllServers(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server details as object
|
||||
*/
|
||||
getServer(serverName){
|
||||
var i;
|
||||
var servers = this.getAllServers();
|
||||
for (i = 0; i < servers.length; i++) {
|
||||
if(servers[i].serverName == serverName){
|
||||
return servers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all servers
|
||||
*/
|
||||
getAllServers(){
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
return storageObj.servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save language for this client
|
||||
*/
|
||||
setLanguage(lang){
|
||||
//Retrieve saved data
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
storageObj.language = lang;
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage.WebConsole = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get saved language for this client
|
||||
*/
|
||||
getLanguage(){
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
if(!storageObj.language)
|
||||
return "en_US";
|
||||
return storageObj.language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create server list if not defined
|
||||
*/
|
||||
initializeLocalStorage(){
|
||||
if (typeof window.localStorage.WebConsole === 'undefined') {
|
||||
//Create empty object
|
||||
var storageObj = new Object();
|
||||
storageObj.servers = new Array();
|
||||
storageObj.language = "en_US";
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage.WebConsole = JSON.stringify(storageObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all server list with provided list
|
||||
*/
|
||||
replaceAllServers(newServerList){
|
||||
//Retrieve saved data
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
storageObj.servers = newServerList;
|
||||
|
||||
//Save to WebStorage
|
||||
window.localStorage.WebConsole = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create settings object if not defined or populate with new options if updating
|
||||
*/
|
||||
initializeSettings(){
|
||||
this.initializeLocalStorage();
|
||||
|
||||
//Create settings object
|
||||
var currentSettings = JSON.parse(window.localStorage.WebConsole).settings;
|
||||
if (typeof currentSettings === 'undefined') {
|
||||
currentSettings = new Object();
|
||||
}
|
||||
|
||||
//Setting array initialization. If you need to add more settings, add them here. Any object is valid as a value (not only bool)
|
||||
var settings = {
|
||||
dateTimePrefix : new Setting("dateTimePrefix", true),
|
||||
retrieveLogFile : new Setting("retrieveLogFile", true)
|
||||
}
|
||||
|
||||
//Set settings
|
||||
jQuery.each(settings, (key, settingObj) =>{
|
||||
if(!currentSettings.hasOwnProperty(settingObj.name))
|
||||
currentSettings[settingObj.name] = settingObj.defaultValue;
|
||||
});
|
||||
|
||||
//Save all
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
storageObj.settings = currentSettings;
|
||||
window.localStorage.WebConsole = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update setting value
|
||||
*/
|
||||
setSetting(name, value){
|
||||
var currentSettings = JSON.parse(window.localStorage.WebConsole).settings;
|
||||
currentSettings[name] = value;
|
||||
|
||||
//Save all
|
||||
var storageObj = JSON.parse(window.localStorage.WebConsole);
|
||||
storageObj.settings = currentSettings;
|
||||
window.localStorage.WebConsole = JSON.stringify(storageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setting value
|
||||
*/
|
||||
getSetting(name){
|
||||
return JSON.parse(window.localStorage.WebConsole).settings[name];
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
class Setting{
|
||||
constructor(name, defaultValue){
|
||||
this.name = name;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
class WSServer{
|
||||
constructor(serverName, serverURI){
|
||||
this.serverName = serverName;
|
||||
this.serverURI = serverURI;
|
||||
}
|
||||
|
||||
setPassword(pwd){
|
||||
this.serverPassword = pwd;
|
||||
}
|
||||
}
|
14
client/src/app/_dto/ActiveConnectionDto.ts
Normal file
14
client/src/app/_dto/ActiveConnectionDto.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Subject } from "rxjs/internal/Subject";
|
||||
import { WebSocketCommand } from "./command/WebSocketCommand";
|
||||
import { ConnectionStatusEnum } from "./ConnectionStatusEnum";
|
||||
import { WebSocketResponse } from "./response/WebSocketResponse";
|
||||
|
||||
export interface ActiveConnectionDto {
|
||||
serverName: string;
|
||||
subject$: Subject<WebSocketResponse>;
|
||||
connectionStatus: ConnectionStatusEnum;
|
||||
receivedMessages: WebSocketResponse[];
|
||||
sentCommands: WebSocketCommand[];
|
||||
isLoggedIn: boolean;
|
||||
token?: string;
|
||||
}
|
5
client/src/app/_dto/ConnectionStatusEnum.ts
Normal file
5
client/src/app/_dto/ConnectionStatusEnum.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum ConnectionStatusEnum {
|
||||
Connecting = 1,
|
||||
Connected = 2,
|
||||
Disconnected = 3
|
||||
}
|
5
client/src/app/_dto/ServerDto.ts
Normal file
5
client/src/app/_dto/ServerDto.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface ServerDto {
|
||||
serverName: string;
|
||||
serverURI: string;
|
||||
serverPassword?: string;
|
||||
}
|
6
client/src/app/_dto/SettingsDto.ts
Normal file
6
client/src/app/_dto/SettingsDto.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface SettingsDto {
|
||||
dateTimePrefix: boolean;
|
||||
retrieveLogFile: boolean;
|
||||
blurryUri: boolean;
|
||||
widerViewport: boolean;
|
||||
}
|
8
client/src/app/_dto/StoredDataDto.ts
Normal file
8
client/src/app/_dto/StoredDataDto.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { ServerDto } from "./ServerDto";
|
||||
import { SettingsDto } from "./SettingsDto";
|
||||
|
||||
export interface StoredDataDto {
|
||||
servers: ServerDto[];
|
||||
language: string;
|
||||
settings: SettingsDto;
|
||||
}
|
5
client/src/app/_dto/command/WebSocketCommand.ts
Normal file
5
client/src/app/_dto/command/WebSocketCommand.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface WebSocketCommand {
|
||||
command: string;
|
||||
params?: string;
|
||||
token?: string;
|
||||
}
|
9
client/src/app/_dto/command/WebSocketCommandEnum.ts
Normal file
9
client/src/app/_dto/command/WebSocketCommandEnum.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export enum WebSocketCommandEnum {
|
||||
Login = "LOGIN",
|
||||
Exec = "EXEC",
|
||||
Players = "PLAYERS",
|
||||
CpuUsage = "CPUUSAGE",
|
||||
RamUsage = "RAMUSAGE",
|
||||
Tps = "TPS",
|
||||
ReadLogFile = "READLOGFILE"
|
||||
}
|
6
client/src/app/_dto/response/ConsoleOutputResponse.ts
Normal file
6
client/src/app/_dto/response/ConsoleOutputResponse.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface ConsoleOutputResponse extends WebSocketResponse{
|
||||
time: string;
|
||||
message: string;
|
||||
}
|
5
client/src/app/_dto/response/CpuResponse.ts
Normal file
5
client/src/app/_dto/response/CpuResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface CpuResponse extends WebSocketResponse{
|
||||
usage: number;
|
||||
}
|
8
client/src/app/_dto/response/LoggedInResponse.ts
Normal file
8
client/src/app/_dto/response/LoggedInResponse.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface LoggedInResponse extends WebSocketResponse{
|
||||
respondsTo: string;
|
||||
username: string;
|
||||
as: string;
|
||||
token: string;
|
||||
}
|
5
client/src/app/_dto/response/LoginRequiredResponse.ts
Normal file
5
client/src/app/_dto/response/LoginRequiredResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface LoginRequiredResponse extends WebSocketResponse{
|
||||
|
||||
}
|
7
client/src/app/_dto/response/PlayersResponse.ts
Normal file
7
client/src/app/_dto/response/PlayersResponse.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface PlayersResponse extends WebSocketResponse {
|
||||
connectedPlayers: number;
|
||||
maxPlayers: number;
|
||||
players: string[];
|
||||
}
|
7
client/src/app/_dto/response/RamResponse.ts
Normal file
7
client/src/app/_dto/response/RamResponse.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface RamResponse extends WebSocketResponse{
|
||||
free: number;
|
||||
used: number;
|
||||
max: number;
|
||||
}
|
5
client/src/app/_dto/response/TpsResponse.ts
Normal file
5
client/src/app/_dto/response/TpsResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface TpsResponse extends WebSocketResponse{
|
||||
tps: number;
|
||||
}
|
5
client/src/app/_dto/response/UnknownCommandResponse.ts
Normal file
5
client/src/app/_dto/response/UnknownCommandResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { WebSocketResponse } from "./WebSocketResponse";
|
||||
|
||||
export interface UnknownCommandResponse extends WebSocketResponse{
|
||||
respondsTo: string;
|
||||
}
|
5
client/src/app/_dto/response/WebSocketResponse.ts
Normal file
5
client/src/app/_dto/response/WebSocketResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface WebSocketResponse {
|
||||
status: number;
|
||||
statusDescription: string;
|
||||
message: string;
|
||||
}
|
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
|
||||
}
|
205
client/src/app/_services/webconsole.service.ts
Normal file
205
client/src/app/_services/webconsole.service.ts
Normal file
@ -0,0 +1,205 @@
|
||||
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]?.close();
|
||||
this.webSocketClients[serverName] = undefined;
|
||||
this.activeConnections = this.activeConnections.filter(e => e.serverName !== serverName);
|
||||
this.activeConnectionsChangedSubject$.next();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
client/src/app/app-routing.module.ts
Normal file
30
client/src/app/app-routing.module.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { LayoutComponent } from './core/layout/layout.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LayoutComponent,
|
||||
children: [
|
||||
//Content
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () =>
|
||||
import('./content/content.module').then(
|
||||
(m) => m.ContentModule
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: ''
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
1
client/src/app/app.component.html
Normal file
1
client/src/app/app.component.html
Normal file
@ -0,0 +1 @@
|
||||
<router-outlet></router-outlet>
|
0
client/src/app/app.component.scss
Normal file
0
client/src/app/app.component.scss
Normal file
35
client/src/app/app.component.spec.ts
Normal file
35
client/src/app/app.component.spec.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'WebConsoleClient'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('WebConsoleClient');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('WebConsoleClient app is running!');
|
||||
});
|
||||
});
|
10
client/src/app/app.component.ts
Normal file
10
client/src/app/app.component.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'WebConsole Client';
|
||||
}
|
26
client/src/app/app.module.ts
Normal file
26
client/src/app/app.module.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { LanguageModule } from './core/language.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
SharedModule,
|
||||
CoreModule,
|
||||
HttpClientModule,
|
||||
LanguageModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
47
client/src/app/content/add-server/add-server.component.html
Normal file
47
client/src/app/content/add-server/add-server.component.html
Normal file
@ -0,0 +1,47 @@
|
||||
<h1 *ngIf="!asModal">{{ "ADDEDITSERVER.AddNewServer" | translate }}</h1>
|
||||
<form class="row g-3" [formGroup]="addServerFormGroup" (ngSubmit)="saveServer()">
|
||||
<div class="col-12">
|
||||
<label for="inputName" class="form-label">{{ "ADDEDITSERVER.Name" | translate }}</label>
|
||||
<input type="text" class="form-control" id="inputName" placeholder="{{ 'ADDEDITSERVER.NamePlaceholder' | translate }}" formControlName="serverNameControl"
|
||||
[class.is-invalid]="addServerFormGroup.get('serverNameControl')?.invalid && (addServerFormGroup.get('serverNameControl')?.dirty || addServerFormGroup.get('serverNameControl')?.touched)">
|
||||
<div class="invalid-feedback">
|
||||
{{ "ADDEDITSERVER.RequiredOrTooLongField" | translate }}
|
||||
</div>
|
||||
<div *ngIf="serverAlreadyExists" class="text-danger">
|
||||
{{ "ADDEDITSERVER.ServerAlreadyExist" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-10">
|
||||
<label for="inputIp" class="form-label">{{ "ADDEDITSERVER.Ip" | translate }}</label>
|
||||
<input type="text" class="form-control" id="inputIp" placeholder="{{ 'ADDEDITSERVER.IpPlaceholder' | translate }}" formControlName="serverIpControl"
|
||||
[class.is-invalid]="addServerFormGroup.get('serverIpControl')?.invalid && (addServerFormGroup.get('serverIpControl')?.dirty || addServerFormGroup.get('serverIpControl')?.touched)">
|
||||
<div class="invalid-feedback">
|
||||
{{ "ADDEDITSERVER.RequiredField" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<label for="inputPort" class="form-label">{{ "ADDEDITSERVER.Port" | translate }}</label>
|
||||
<input type="number" class="form-control" id="inputPort" formControlName="serverPortControl"
|
||||
[class.is-invalid]="addServerFormGroup.get('serverPortControl')?.invalid && (addServerFormGroup.get('serverPortControl')?.dirty || addServerFormGroup.get('serverPortControl')?.touched)">
|
||||
<div class="invalid-feedback">
|
||||
{{ "ADDEDITSERVER.InvalidPort" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="inputPassword" class="form-label">{{ "ADDEDITSERVER.Password" | translate }}</label>
|
||||
<input type="password" class="form-control" id="inputPassword" placeholder="{{ 'ADDEDITSERVER.PasswordPlaceholder' | translate }}" formControlName="serverPasswordControl"
|
||||
[class.is-invalid]="addServerFormGroup.get('serverPasswordControl')?.invalid && (addServerFormGroup.get('serverPasswordControl')?.dirty || addServerFormGroup.get('serverPasswordControl')?.touched)">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sslCheck" formControlName="serverSslEnabledControl">
|
||||
<label class="form-check-label" for="sslCheck">
|
||||
{{ "ADDEDITSERVER.SslEnabled" | translate }}
|
||||
</label>
|
||||
<p *ngIf="isClientOverHttps" class="text-warning">{{ "ADDEDITSERVER.SslEnabledMandatory" | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!asModal" class="col-12">
|
||||
<button type="submit" class="btn btn-primary">{{ "ADDEDITSERVER.Add" | translate }}</button>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AddServerComponent } from './add-server.component';
|
||||
|
||||
describe('AddServerComponent', () => {
|
||||
let component: AddServerComponent;
|
||||
let fixture: ComponentFixture<AddServerComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AddServerComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AddServerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
72
client/src/app/content/add-server/add-server.component.ts
Normal file
72
client/src/app/content/add-server/add-server.component.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { StorageService } from 'src/app/_services/storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-server',
|
||||
templateUrl: './add-server.component.html',
|
||||
styleUrls: ['./add-server.component.scss']
|
||||
})
|
||||
export class AddServerComponent implements OnInit {
|
||||
|
||||
@Input() asModal: boolean = false; //If component is being rendered inside a modal
|
||||
|
||||
//SSL detected
|
||||
isClientOverHttps: boolean = location.protocol == 'https:';
|
||||
|
||||
//Add server FormGroup
|
||||
addServerFormGroup = new FormGroup({
|
||||
serverNameControl: new FormControl('', [Validators.required, Validators.maxLength(50)]),
|
||||
serverIpControl: new FormControl('', [Validators.required]),
|
||||
serverPortControl: new FormControl(8080, [Validators.required, Validators.min(0), Validators.max(99999)]),
|
||||
serverPasswordControl: new FormControl(''),
|
||||
serverSslEnabledControl: new FormControl({ value: location.protocol == 'https:', disabled: location.protocol == 'https:' }),
|
||||
});
|
||||
|
||||
//A server with this name was found during save operation
|
||||
serverAlreadyExists: boolean = false;
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
saveServer(modal?: any): void {
|
||||
//If form is invalid, stop saving operation
|
||||
if (!this.addServerFormGroup.valid) {
|
||||
this.addServerFormGroup.markAllAsTouched()
|
||||
return;
|
||||
}
|
||||
|
||||
//Get form information
|
||||
const serverName: string = this.addServerFormGroup.get("serverNameControl")?.value.replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "").replace(/"/g, "");
|
||||
const serverIp: string = this.addServerFormGroup.get("serverIpControl")?.value;
|
||||
const serverPort: string = this.addServerFormGroup.get("serverPortControl")?.value;
|
||||
const serverPassword: string = this.addServerFormGroup.get("serverPasswordControl")?.value || undefined;
|
||||
const serverSsl: boolean = this.addServerFormGroup.get("serverSslEnabledControl")?.value;
|
||||
|
||||
//If a server with this same name exists, stop saving operation
|
||||
this.serverAlreadyExists = this.storageService.getAllServers().find(e => e.serverName == serverName) != undefined;
|
||||
if (this.serverAlreadyExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Build URI
|
||||
let uri;
|
||||
if (serverSsl) {
|
||||
uri = "wss://" + serverIp + ":" + serverPort;
|
||||
} else {
|
||||
uri = "ws://" + serverIp + ":" + serverPort;
|
||||
}
|
||||
|
||||
//Save server
|
||||
this.storageService.saveServer(serverName, uri, serverPassword);
|
||||
|
||||
//If component is being shown in a modal, close it
|
||||
if (this.asModal)
|
||||
modal?.close('Save server');
|
||||
}
|
||||
|
||||
}
|
130
client/src/app/content/console/console.component.html
Normal file
130
client/src/app/content/console/console.component.html
Normal file
@ -0,0 +1,130 @@
|
||||
<!-- Server name, insights toggler and badges -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1>{{ server.serverName }}</h1>
|
||||
</div>
|
||||
<div class="col-md-auto d-flex align-self-center">
|
||||
<button type="button" class="btn btn-outline-primary" *ngIf="showConsole" (click)="showServerInfo = !showServerInfo">
|
||||
<fa-icon *ngIf="!showServerInfo" [icon]="icons.faEye"></fa-icon>
|
||||
<fa-icon *ngIf="showServerInfo" [icon]="icons.faEyeSlash"></fa-icon> {{ "CONSOLE.ToggleServerInfo" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success me-1" *ngIf="activeConnection.connectionStatus == 2">{{ "CONSOLE.Connected" | translate }}</span>
|
||||
<span class="badge bg-danger mb-3" *ngIf="activeConnection.connectionStatus == 3 && showConsole">{{ "CONSOLE.Disconnected" | translate }}</span>
|
||||
<span class="badge bg-secondary mb-3" *ngIf="activeConnection.connectionStatus == 2 && loggedInUsername">{{ "CONSOLE.LoggedInAs" | translate }} {{ loggedInUsername }} ({{ loggedInAs }})</span>
|
||||
|
||||
<!-- Connected collapsable: Only shown when connection is or was stablished -->
|
||||
<div #collapseGlobal="ngbCollapse" [(ngbCollapse)]="!showConsole">
|
||||
<!-- Server insights -->
|
||||
<div class="row" #collapse="ngbCollapse" [(ngbCollapse)]="!showServerInfo">
|
||||
<div class="col-lg-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ "CONSOLE.PlayersOnline" | translate }}</h5>
|
||||
<p class="card-text">{{ connectedPlayers }} / {{ maxPlayers }}</p>
|
||||
<p class="card-text">
|
||||
<ngb-progressbar type="secondary" [value]="connectedPlayers*100/maxPlayers" [striped]="true" [animated]="true"></ngb-progressbar>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ "CONSOLE.CpuUsage" | translate }}</h5>
|
||||
<p class="card-text">{{ cpuUsage }}%</p>
|
||||
<p class="card-text">
|
||||
<ngb-progressbar [type]="cpuUsage>80 ? 'danger' : 'secondary'" [value]="cpuUsage" [striped]="true" [animated]="true"></ngb-progressbar>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ "CONSOLE.RamUsage" | translate }}</h5>
|
||||
<p class="card-text">{{ ramUsed }} MB / {{ ramMax }} MB</p>
|
||||
<p class="card-text">
|
||||
<ngb-progressbar [type]="ramUsed*100/ramMax>80 ? 'danger' : 'secondary'" [value]="ramUsed*100/ramMax" [striped]="true" [animated]="true"></ngb-progressbar>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ "CONSOLE.Tps" | translate }}</h5>
|
||||
<p class="card-text">{{ tps }} / 20</p>
|
||||
<p class="card-text">
|
||||
<ngb-progressbar [type]="tps*100/20>80 ? 'success' : 'secondary'" [value]="tps*100/20" [striped]="true" [animated]="true"></ngb-progressbar>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Log in button -->
|
||||
<div class="row" *ngIf="!activeConnection.isLoggedIn">
|
||||
<div class="col-12">
|
||||
<button type="button" class="btn btn-warning w-100" (click)="requestPassword()">
|
||||
<fa-icon [icon]="icons.faLock"></fa-icon> {{ "CONSOLE.ClickToLogin" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Console -->
|
||||
<div class="card mt-3 mb-3">
|
||||
<div #consoleDiv class="card-body overflow-auto text-light bg-dark console" [innerHTML]="consoleHtml | sanitize" [scrollTop]="keepScrollDown ? consoleDiv.scrollHeight : consoleDiv.scrollTop"></div>
|
||||
</div>
|
||||
|
||||
<!-- Command Input -->
|
||||
<div class="input-group mb-3">
|
||||
<input #commandInput type="text" class="form-control" aria-label="Command" aria-describedby="button-command" (keyup)="onKeyUpCommandInput($event)" [disabled]="activeConnection.connectionStatus != 2">
|
||||
<button class="btn btn-secondary" type="button" id="button-command" (click)="sendCommand()" [disabled]="activeConnection.connectionStatus != 2">{{ "CONSOLE.Send" | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Connecting spinner -->
|
||||
<div class="d-flex flex-column min-vh-100 align-items-center" *ngIf="activeConnection.connectionStatus == 1">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">{{ "GENERAL.Loading" | translate }}</span>
|
||||
</div>
|
||||
<p>{{ "CONSOLE.Connecting" | translate }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Disconnected -->
|
||||
<div class="d-flex flex-column min-vh-100 align-items-center" *ngIf="activeConnection.connectionStatus == 3 && !showConsole">
|
||||
<fa-icon [icon]="icons.faXmark" size="3x"></fa-icon>
|
||||
<p class="mb-0">{{ "CONSOLE.CannotConnect" | translate }}</p>
|
||||
<p class="mb-0">{{ "CONSOLE.CannotConnectDescription1" | translate }} <a href="https://www.yougetsignal.com/tools/open-ports/" target="_blank">{{ "CONSOLE.Tool" | translate }}</a> {{ "CONSOLE.CannotConnectDescription2" | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Password modal -->
|
||||
<ng-template #setPasswordModal let-modal>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{ "CONSOLE.PasswordRequested" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="modal.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-3">
|
||||
<div class="col-12">
|
||||
<input #passwordInput type="password" class="form-control" (keyup.enter)="setPassword(passwordInput.value, rememberInput.checked);modal.close();">
|
||||
<small *ngIf="savedPasswordSent" class="form-text text-danger">{{ "CONSOLE.WrongPassword" | translate }}</small>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input #rememberInput class="form-check-input" type="checkbox" id="rememberCheck">
|
||||
<label class="form-check-label" for="rememberCheck">
|
||||
{{ "CONSOLE.RememberPassword" | translate }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" (click)="setPassword(passwordInput.value, rememberInput.checked);modal.close();">{{ "CONSOLE.Connect" | translate }}</button>
|
||||
</div>
|
||||
</ng-template>
|
3
client/src/app/content/console/console.component.scss
Normal file
3
client/src/app/content/console/console.component.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.console {
|
||||
height: 480px;
|
||||
}
|
25
client/src/app/content/console/console.component.spec.ts
Normal file
25
client/src/app/content/console/console.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ConsoleComponent } from './console.component';
|
||||
|
||||
describe('ConsoleComponent', () => {
|
||||
let component: ConsoleComponent;
|
||||
let fixture: ComponentFixture<ConsoleComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ConsoleComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ConsoleComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
378
client/src/app/content/console/console.component.ts
Normal file
378
client/src/app/content/console/console.component.ts
Normal file
@ -0,0 +1,378 @@
|
||||
import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Icons } from 'src/app/shared/icons';
|
||||
import { ActiveConnectionDto } from 'src/app/_dto/ActiveConnectionDto';
|
||||
import { WebSocketCommand } from 'src/app/_dto/command/WebSocketCommand';
|
||||
import { WebSocketCommandEnum } from 'src/app/_dto/command/WebSocketCommandEnum';
|
||||
import { ConnectionStatusEnum } from 'src/app/_dto/ConnectionStatusEnum';
|
||||
import { ConsoleOutputResponse } from 'src/app/_dto/response/ConsoleOutputResponse';
|
||||
import { CpuResponse } from 'src/app/_dto/response/CpuResponse';
|
||||
import { LoggedInResponse } from 'src/app/_dto/response/LoggedInResponse';
|
||||
import { LoginRequiredResponse } from 'src/app/_dto/response/LoginRequiredResponse';
|
||||
import { PlayersResponse } from 'src/app/_dto/response/PlayersResponse';
|
||||
import { RamResponse } from 'src/app/_dto/response/RamResponse';
|
||||
import { TpsResponse } from 'src/app/_dto/response/TpsResponse';
|
||||
import { UnknownCommandResponse } from 'src/app/_dto/response/UnknownCommandResponse';
|
||||
import { WebSocketResponse } from 'src/app/_dto/response/WebSocketResponse';
|
||||
import { ServerDto } from 'src/app/_dto/ServerDto';
|
||||
import { SettingsEnum, StorageService } from 'src/app/_services/storage.service';
|
||||
import { WebconsoleService } from 'src/app/_services/webconsole.service';
|
||||
import { AnsiUp } from "ansi_up"
|
||||
|
||||
@Component({
|
||||
selector: 'app-console',
|
||||
templateUrl: './console.component.html',
|
||||
styleUrls: ['./console.component.scss']
|
||||
})
|
||||
export class ConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
//General stuff
|
||||
icons = Icons;
|
||||
server!: ServerDto; //Server info
|
||||
activeConnection!: ActiveConnectionDto; //Active connection object (which stores messages received, messages sent, subject, etc.)
|
||||
subscription!: Subscription; //Current subscription by this component
|
||||
//Content of the console
|
||||
@ViewChild("consoleDiv", { static: false }) consoleDiv!: ElementRef;
|
||||
consoleHtml: string = "";
|
||||
//Password modal
|
||||
@ViewChild("setPasswordModal", { static: false }) passwordModal!: ElementRef;
|
||||
//Command input
|
||||
@ViewChild("commandInput", { static: false }) commandInput!: ElementRef;
|
||||
//Server Insights
|
||||
connectedPlayers: number = 0;
|
||||
maxPlayers: number = 0;
|
||||
cpuUsage: number = 0;
|
||||
ramFree: number = 0;
|
||||
ramUsed: number = 0;
|
||||
ramMax: number = 0;
|
||||
tps: number = 0;
|
||||
|
||||
//Helper properties
|
||||
keepScrollDown: boolean = true;
|
||||
showServerInfo: boolean = true;
|
||||
showConsole: boolean = false;
|
||||
loggedInUsername: string = "";
|
||||
loggedInAs: string = "";
|
||||
savedPasswordSent: boolean = false;
|
||||
browsingCommandHistoryIndex: number = -1;
|
||||
insightsInverval!: any;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private storageService: StorageService,
|
||||
private webConsoleService: WebconsoleService,
|
||||
private modalService: NgbModal,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* On component initialization, connect to WebSocket server and subscribe to subjects (where WebSocket messages are received)
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
console.log("Init console component");
|
||||
//Get server name from params
|
||||
const routeParams: ParamMap = this.route.snapshot.paramMap;
|
||||
const serverName = routeParams.get('serverName');
|
||||
|
||||
//If server name not provided, throw error and redirect to homepage
|
||||
if (!serverName) {
|
||||
this.router.navigate(['']);
|
||||
throw Error("Server name not provided");
|
||||
}
|
||||
|
||||
//Get server from its name. If not found, redirect to homepage
|
||||
const serverObject = this.storageService.getServer(serverName);
|
||||
|
||||
if (!serverObject) {
|
||||
this.router.navigate(['']);
|
||||
throw Error("Server name invalid");
|
||||
}
|
||||
|
||||
//Save server object and connect
|
||||
this.server = serverObject;
|
||||
|
||||
//Connect to server
|
||||
this.activeConnection = this.webConsoleService.connect(this.server.serverName);
|
||||
this.showConsole = this.activeConnection.connectionStatus == ConnectionStatusEnum.Connected;
|
||||
|
||||
//Process old messages (In case we are resuming a session)
|
||||
this.activeConnection.receivedMessages.forEach(e => this.processMessage(e));
|
||||
|
||||
//If not created, create the Players, CPU, RAM and TPS interval
|
||||
if (!this.insightsInverval) {
|
||||
this.insightsInverval = setInterval(() => {
|
||||
this.requestServerInsights();
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
//Subscribe to Subject to process received messages
|
||||
this.subscription = this.activeConnection.subject$.subscribe({
|
||||
next: (msg: WebSocketResponse) => {
|
||||
this.showConsole = true;
|
||||
this.processMessage(msg);
|
||||
},
|
||||
complete: () => {
|
||||
//Disconnected from server
|
||||
this.showServerInfo = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
//Scroll down console
|
||||
setTimeout(() => this.consoleDiv.nativeElement.scrollTop = this.consoleDiv?.nativeElement.scrollHeight)
|
||||
}
|
||||
|
||||
/**
|
||||
* On component destruction, unsubscribe to subject
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
//Stop insights
|
||||
clearInterval(this.insightsInverval);
|
||||
//Remove subscription as this component is going mayday
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a new message from WebSockets
|
||||
* @param response WebSocket message
|
||||
*/
|
||||
processMessage(response: WebSocketResponse): void {
|
||||
// console.log(`Received message from WebSocket (${this.server.serverName}): `, msg);
|
||||
let r;
|
||||
switch (response.status) {
|
||||
case 10:
|
||||
//Console output
|
||||
r = response as ConsoleOutputResponse;
|
||||
this.writeToWebConsole(r.message, r.time);
|
||||
break;
|
||||
case 200:
|
||||
//LoggedIn
|
||||
r = response as LoggedInResponse;
|
||||
this.loggedInUsername = r.username;
|
||||
this.loggedInAs = r.as;
|
||||
break;
|
||||
case 400:
|
||||
//Unknown
|
||||
r = response as UnknownCommandResponse;
|
||||
console.log("400 Unknown Comamnd", r);
|
||||
break;
|
||||
case 401:
|
||||
//Login Required
|
||||
r = response as LoginRequiredResponse;
|
||||
if (!this.activeConnection.isLoggedIn) {
|
||||
if (this.server.serverPassword && !this.savedPasswordSent) {
|
||||
this.savedPasswordSent = true;
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.Login, this.server.serverPassword);
|
||||
} else {
|
||||
this.requestPassword();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1000:
|
||||
//Players
|
||||
r = response as PlayersResponse;
|
||||
this.connectedPlayers = r.connectedPlayers;
|
||||
this.maxPlayers = r.maxPlayers;
|
||||
break;
|
||||
case 1001:
|
||||
//CPU Usage
|
||||
r = response as CpuResponse;
|
||||
this.cpuUsage = r.usage;
|
||||
break;
|
||||
case 1002:
|
||||
//RAM usage
|
||||
r = response as RamResponse;
|
||||
this.ramFree = r.free;
|
||||
this.ramUsed = r.used;
|
||||
this.ramMax = r.max;
|
||||
break;
|
||||
case 1003:
|
||||
//TPS
|
||||
r = response as TpsResponse;
|
||||
this.tps = r.tps;
|
||||
break;
|
||||
default:
|
||||
//Not recognised response
|
||||
console.error("Unrecognised response:", response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize and print message to console
|
||||
* @param msg Message to print
|
||||
* @param time Time, if applicable
|
||||
*/
|
||||
private writeToWebConsole(msg: string, time: string) {
|
||||
this.keepScrollDown = this.consoleDiv?.nativeElement.scrollHeight - this.consoleDiv?.nativeElement.scrollTop === this.consoleDiv?.nativeElement.clientHeight;
|
||||
|
||||
//Write to div, replacing < to < (to avoid XSS) and replacing new line to br.
|
||||
msg = msg.replace(/</g, "<");
|
||||
msg = msg.replace(/(?:\r\n|\r|\n)/g, "<br>");
|
||||
|
||||
//Color filter for MC codes.
|
||||
msg = msg.replace(/§0/g, "<span style='color: #000000;'>"); //&0
|
||||
msg = msg.replace(/§1/g, "<span style='color: #0000AA;'>"); //&1
|
||||
msg = msg.replace(/§2/g, "<span style='color: #00AA00;'>"); //&2
|
||||
msg = msg.replace(/§3/g, "<span style='color: #00AAAA;'>"); //&3
|
||||
msg = msg.replace(/§4/g, "<span style='color: #AA0000;'>"); //&4
|
||||
msg = msg.replace(/§5/g, "<span style='color: #AA00AA;'>"); //&5
|
||||
msg = msg.replace(/§6/g, "<span style='color: #FFAA00;'>"); //&6
|
||||
msg = msg.replace(/§7/g, "<span style='color: #AAAAAA;'>"); //&7
|
||||
msg = msg.replace(/§8/g, "<span style='color: #555555;'>"); //&8
|
||||
msg = msg.replace(/§9/g, "<span style='color: #5555FF;'>"); //&9
|
||||
msg = msg.replace(/§a/g, "<span style='color: #55FF55;'>"); //&a
|
||||
msg = msg.replace(/§b/g, "<span style='color: #55FFFF;'>"); //&b
|
||||
msg = msg.replace(/§c/g, "<span style='color: #FF5555;'>"); //&c
|
||||
msg = msg.replace(/§d/g, "<span style='color: #FF55FF;'>"); //&d
|
||||
msg = msg.replace(/§e/g, "<span style='color: #FFFF55;'>"); //&e
|
||||
msg = msg.replace(/§f/g, "<span style='color: #FFFFFF;'>"); //&f
|
||||
|
||||
msg = msg.replace(/§l/g, "<span style='font-weight:bold;'>"); //&l
|
||||
msg = msg.replace(/§m/g, "<span style='text-decoration: line-through;'>"); //&m
|
||||
msg = msg.replace(/§n/g, "<span style='text-decoration: underline;'>"); //&n
|
||||
msg = msg.replace(/§o/g, "<span style='font-style: italic;'>"); //&o
|
||||
|
||||
msg = msg.replace(/§r/g, "</span>"); //&r
|
||||
|
||||
//Color filter for MC 1.18 (Also easy :D)
|
||||
msg = msg.replace(/0/g, "<span style='color: #000000;'>"); //&0
|
||||
msg = msg.replace(/1/g, "<span style='color: #0000AA;'>"); //&1
|
||||
msg = msg.replace(/2/g, "<span style='color: #00AA00;'>"); //&2
|
||||
msg = msg.replace(/3/g, "<span style='color: #00AAAA;'>"); //&3
|
||||
msg = msg.replace(/4/g, "<span style='color: #AA0000;'>"); //&4
|
||||
msg = msg.replace(/5/g, "<span style='color: #AA00AA;'>"); //&5
|
||||
msg = msg.replace(/6/g, "<span style='color: #FFAA00;'>"); //&6
|
||||
msg = msg.replace(/7/g, "<span style='color: #AAAAAA;'>"); //&7
|
||||
msg = msg.replace(/8/g, "<span style='color: #555555;'>"); //&8
|
||||
msg = msg.replace(/9/g, "<span style='color: #5555FF;'>"); //&9
|
||||
msg = msg.replace(/a/g, "<span style='color: #55FF55;'>"); //&a
|
||||
msg = msg.replace(/b/g, "<span style='color: #55FFFF;'>"); //&b
|
||||
msg = msg.replace(/c/g, "<span style='color: #FF5555;'>"); //&c
|
||||
msg = msg.replace(/d/g, "<span style='color: #FF55FF;'>"); //&d
|
||||
msg = msg.replace(/e/g, "<span style='color: #FFFF55;'>"); //&e
|
||||
msg = msg.replace(/f/g, "<span style='color: #FFFFFF;'>"); //&f
|
||||
|
||||
msg = msg.replace(/l/g, "<span style='font-weight:bold;'>"); //&l
|
||||
msg = msg.replace(/m/g, "<span style='text-decoration: line-through;'>"); //&m
|
||||
msg = msg.replace(/n/g, "<span style='text-decoration: underline;'>"); //&n
|
||||
msg = msg.replace(/o/g, "<span style='font-style: italic;'>"); //&o
|
||||
|
||||
msg = msg.replace(/r/g, "</span>"); //&r
|
||||
|
||||
// ANSI Processing
|
||||
var ansi_up = new AnsiUp();
|
||||
msg = ansi_up.ansi_to_html(msg);
|
||||
|
||||
//Append datetime if enabled
|
||||
if (this.storageService.getSetting(SettingsEnum.DateTimePrefix)) {
|
||||
if (typeof time !== 'undefined' && time !== null) //if time is present and not null
|
||||
msg = "[" + time + "] " + msg;
|
||||
else if (typeof time !== 'undefined' && time === null) //if time is present and null
|
||||
null; //no time (is already printed)
|
||||
else
|
||||
msg = "[" + new Date().toLocaleTimeString() + "] " + msg;
|
||||
}
|
||||
|
||||
//Append HTML
|
||||
const spanCount = (msg.match(/<span /g) || []).length; //Number of times a color is applied
|
||||
const spanCloseCount = (msg.match(/<\/span> /g) || []).length; //Number of already existing </span>
|
||||
const numberOfUnclosedSpans: number = spanCount - spanCloseCount; //Number of </span> pending to be closed
|
||||
this.consoleHtml += msg + ("</span>".repeat(numberOfUnclosedSpans)) + "<br>"; //Append to console the message, plus the required </span>'s, plus a line break
|
||||
}
|
||||
|
||||
/**
|
||||
* Open password request modal
|
||||
*/
|
||||
requestPassword(): void {
|
||||
this.modalService.open(this.passwordModal, { size: 'md' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to login against server
|
||||
* @param password Password to send
|
||||
* @param rememberPassword If true, save password in localStorage
|
||||
*/
|
||||
setPassword(password: string, rememberPassword: boolean): void {
|
||||
//Edit server if remember password checkbox is checked
|
||||
if (rememberPassword)
|
||||
this.storageService.saveServer(this.server.serverName, this.server.serverURI, password);
|
||||
|
||||
setTimeout(() => this.savedPasswordSent = true, 200)
|
||||
|
||||
//Send login message
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.Login, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send command typed in the command input
|
||||
*/
|
||||
sendCommand(): void {
|
||||
const cmd: string = this.commandInput.nativeElement.value;
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
//Clear input
|
||||
this.commandInput.nativeElement.value = "";
|
||||
this.browsingCommandHistoryIndex = -1;
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.Exec, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a key is pressed in the command input
|
||||
* @param e KeyboardEvent
|
||||
*/
|
||||
onKeyUpCommandInput(e: KeyboardEvent): void {
|
||||
if (e.code === 'Enter') { //Detect enter key
|
||||
this.sendCommand();
|
||||
} else if (e.code === "ArrowUp") { //Replace with older command
|
||||
//Get list of sent commands
|
||||
const sentCommands: WebSocketCommand[] = this.activeConnection.sentCommands.filter(e => e.command === WebSocketCommandEnum.Exec);
|
||||
|
||||
//If no command was sent yet, return
|
||||
if (sentCommands.length == 0)
|
||||
return;
|
||||
|
||||
//If this is the first time arrow up is pressed, start browsing history
|
||||
if (this.browsingCommandHistoryIndex <= 0)
|
||||
this.browsingCommandHistoryIndex = sentCommands.length;
|
||||
|
||||
//Set command in our input component
|
||||
this.commandInput.nativeElement.value = sentCommands[this.browsingCommandHistoryIndex - 1]?.params;
|
||||
this.browsingCommandHistoryIndex = this.browsingCommandHistoryIndex - 1;
|
||||
} else if (e.code === "ArrowDown") { //Replace with newer command
|
||||
//Get list of sent commands
|
||||
const sentCommands: WebSocketCommand[] = this.activeConnection.sentCommands.filter(e => e.command === WebSocketCommandEnum.Exec);
|
||||
|
||||
//If not browsing history, do nothing
|
||||
if (this.browsingCommandHistoryIndex !== -1) {
|
||||
//Go back to index 0 if overflow
|
||||
if (this.browsingCommandHistoryIndex >= sentCommands.length - 1)
|
||||
this.browsingCommandHistoryIndex = -1;
|
||||
|
||||
//Set command in our input component
|
||||
this.commandInput.nativeElement.value = sentCommands[this.browsingCommandHistoryIndex + 1]?.params;
|
||||
this.browsingCommandHistoryIndex = this.browsingCommandHistoryIndex + 1;
|
||||
}
|
||||
} else if (e.code == "tab") { //Detect tab key
|
||||
//Suggest user from connected Players
|
||||
//TODO tab not being detected :(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request server insights
|
||||
*/
|
||||
requestServerInsights(): void {
|
||||
if (this.showServerInfo && this.showConsole && this.activeConnection.connectionStatus == ConnectionStatusEnum.Connected && this.activeConnection.isLoggedIn) {
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.Players);
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.CpuUsage);
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.RamUsage);
|
||||
this.webConsoleService.sendMessage(this.server.serverName, WebSocketCommandEnum.Tps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
18
client/src/app/content/content-routing.module.ts
Normal file
18
client/src/app/content/content-routing.module.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { ConsoleComponent } from './console/console.component';
|
||||
import { IndexComponent } from './index/index.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'console/:serverName', component: ConsoleComponent },
|
||||
{ path: 'settings', component: SettingsComponent },
|
||||
{ path: '', component: IndexComponent },
|
||||
{ path: '**', component: IndexComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ContentRoutingModule { }
|
25
client/src/app/content/content.module.ts
Normal file
25
client/src/app/content/content.module.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ContentRoutingModule } from './content-routing.module';
|
||||
import { IndexComponent } from './index/index.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { AddServerComponent } from './add-server/add-server.component';
|
||||
import { EditServerComponent } from './edit-server/edit-server.component';
|
||||
import { ConsoleComponent } from './console/console.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
IndexComponent,
|
||||
SettingsComponent,
|
||||
AddServerComponent,
|
||||
EditServerComponent,
|
||||
ConsoleComponent
|
||||
],
|
||||
imports: [
|
||||
ContentRoutingModule,
|
||||
SharedModule
|
||||
]
|
||||
})
|
||||
export class ContentModule { }
|
@ -0,0 +1,49 @@
|
||||
<h1 *ngIf="!asModal">{{ "ADDEDITSERVER.EditServer" | translate }}</h1>
|
||||
<form class="row g-3" [formGroup]="editServerFormGroup" (ngSubmit)="saveServer()">
|
||||
<div class="col-12">
|
||||
<label for="inputName" class="form-label">{{ "ADDEDITSERVER.Name" | translate }}</label>
|
||||
<input type="text" class="form-control" id="inputName" placeholder="{{ 'ADDEDITSERVER.NamePlaceholder' | translate }}" formControlName="serverNameControl">
|
||||
<small class="form-text text-warning">{{ "ADDEDITSERVER.NameNotEditable" | translate }}</small>
|
||||
</div>
|
||||
<div class="col-lg-10">
|
||||
<label for="inputIp" class="form-label">{{ "ADDEDITSERVER.Ip" | translate }}</label>
|
||||
<input type="text" class="form-control" id="inputIp" placeholder="{{ 'ADDEDITSERVER.IpPlaceholder' | translate }}" formControlName="serverIpControl"
|
||||
[class.is-invalid]="editServerFormGroup.get('serverIpControl')?.invalid && (editServerFormGroup.get('serverIpControl')?.dirty || editServerFormGroup.get('serverIpControl')?.touched)">
|
||||
<div class="invalid-feedback">
|
||||
{{ "ADDEDITSERVER.RequiredField" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<label for="inputPort" class="form-label">{{ "ADDEDITSERVER.Port" | translate }}</label>
|
||||
<input type="number" class="form-control" id="inputPort" formControlName="serverPortControl"
|
||||
[class.is-invalid]="editServerFormGroup.get('serverPortControl')?.invalid && (editServerFormGroup.get('serverPortControl')?.dirty || editServerFormGroup.get('serverPortControl')?.touched)">
|
||||
<div class="invalid-feedback">
|
||||
{{ "ADDEDITSERVER.InvalidPort" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="inputPassword" class="form-label">{{ "ADDEDITSERVER.Password" | translate }}</label>
|
||||
<input type="password" class="form-control" id="inputPassword" placeholder="{{ 'ADDEDITSERVER.PasswordPlaceholder' | translate }}" formControlName="serverPasswordControl"
|
||||
[class.is-invalid]="editServerFormGroup.get('serverPasswordControl')?.invalid && (editServerFormGroup.get('serverPasswordControl')?.dirty || editServerFormGroup.get('serverPasswordControl')?.touched)">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="updatePasswordCheck" formControlName="keepServerPasswordControl" (change)="onUpdatePasswordCheckboxChange()">
|
||||
<label class="form-check-label" for="updatePasswordCheck">
|
||||
{{ "ADDEDITSERVER.KeepPasswordUnchanged" | translate }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sslCheck" formControlName="serverSslEnabledControl">
|
||||
<label class="form-check-label" for="sslCheck">
|
||||
{{ "ADDEDITSERVER.SslEnabled" | translate }}
|
||||
</label>
|
||||
<p *ngIf="isClientOverHttps" class="text-warning">{{ "ADDEDITSERVER.SslEnabledMandatory" | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!asModal" class="col-12">
|
||||
<button type="submit" class="btn btn-primary">{{ "ADDEDITSERVER.EditServer" | translate }}</button>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditServerComponent } from './edit-server.component';
|
||||
|
||||
describe('EditServerComponent', () => {
|
||||
let component: EditServerComponent;
|
||||
let fixture: ComponentFixture<EditServerComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ EditServerComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditServerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
92
client/src/app/content/edit-server/edit-server.component.ts
Normal file
92
client/src/app/content/edit-server/edit-server.component.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { AfterViewChecked, Component, Input, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { ServerDto } from 'src/app/_dto/ServerDto';
|
||||
import { StorageService } from 'src/app/_services/storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-server',
|
||||
templateUrl: './edit-server.component.html',
|
||||
styleUrls: ['./edit-server.component.scss']
|
||||
})
|
||||
export class EditServerComponent implements OnInit {
|
||||
@Input() asModal: boolean = false; //If component is being rendered inside a modal
|
||||
|
||||
//Name of the server to edit
|
||||
@Input() serverNameBeingEdited!: string;
|
||||
|
||||
//SSL detected
|
||||
isClientOverHttps: boolean = location.protocol == 'https:';
|
||||
|
||||
//Add server FormGroup
|
||||
editServerFormGroup = new FormGroup({
|
||||
serverNameControl: new FormControl({ value: '', disabled: true }, [Validators.required, Validators.maxLength(50)]),
|
||||
serverIpControl: new FormControl('', [Validators.required]),
|
||||
serverPortControl: new FormControl(8080, [Validators.required, Validators.min(0), Validators.max(99999)]),
|
||||
serverPasswordControl: new FormControl({ value: '', disabled: true }),
|
||||
serverSslEnabledControl: new FormControl({ value: location.protocol == 'https:', disabled: location.protocol == 'https:' }),
|
||||
keepServerPasswordControl: new FormControl(true),
|
||||
});
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//Load server info
|
||||
const savedServer: ServerDto | undefined = this.storageService.getServer(this.serverNameBeingEdited);
|
||||
if (savedServer) {
|
||||
//Parse address and port
|
||||
const addressWithPort = savedServer.serverURI.replace("wss://", "").replace("ws://", "");
|
||||
const address = addressWithPort.slice(0, addressWithPort.lastIndexOf(":"));
|
||||
const port = addressWithPort.slice(addressWithPort.lastIndexOf(":") + 1);
|
||||
|
||||
//Set values
|
||||
this.editServerFormGroup.get("serverNameControl")?.setValue(savedServer.serverName);
|
||||
this.editServerFormGroup.get("serverIpControl")?.setValue(address);
|
||||
this.editServerFormGroup.get("serverPortControl")?.setValue(port);
|
||||
savedServer.serverURI.startsWith("wss://")
|
||||
? this.editServerFormGroup.get("serverSslEnabledControl")?.setValue(true)
|
||||
: this.editServerFormGroup.get("serverSslEnabledControl")?.setValue(false)
|
||||
}
|
||||
}
|
||||
|
||||
onUpdatePasswordCheckboxChange(): void {
|
||||
if (this.editServerFormGroup.get("keepServerPasswordControl")?.value){
|
||||
this.editServerFormGroup.get("serverPasswordControl")?.disable();
|
||||
this.editServerFormGroup.get("serverPasswordControl")?.setValue('');
|
||||
}
|
||||
else
|
||||
this.editServerFormGroup.get("serverPasswordControl")?.enable();
|
||||
}
|
||||
|
||||
saveServer(modal?: any): void {
|
||||
//If form is invalid, stop edit operation
|
||||
if (!this.editServerFormGroup.valid) {
|
||||
this.editServerFormGroup.markAllAsTouched()
|
||||
return;
|
||||
}
|
||||
|
||||
//Get form information
|
||||
const serverName: string = this.editServerFormGroup.get("serverNameControl")?.value.replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "").replace(/"/g, "");
|
||||
const serverIp: string = this.editServerFormGroup.get("serverIpControl")?.value;
|
||||
const serverPort: string = this.editServerFormGroup.get("serverPortControl")?.value;
|
||||
const serverPassword: string = this.editServerFormGroup.get("serverPasswordControl")?.value || null;
|
||||
const serverSsl: boolean = this.editServerFormGroup.get("serverSslEnabledControl")?.value;
|
||||
|
||||
//Build URI
|
||||
let uri;
|
||||
if (serverSsl) {
|
||||
uri = "wss://" + serverIp + ":" + serverPort;
|
||||
} else {
|
||||
uri = "ws://" + serverIp + ":" + serverPort;
|
||||
}
|
||||
|
||||
//Save server
|
||||
this.storageService.saveServer(serverName, uri, serverPassword);
|
||||
|
||||
//If component is being shown in a modal, close it
|
||||
if (this.asModal)
|
||||
modal?.close('Save server');
|
||||
}
|
||||
|
||||
}
|
109
client/src/app/content/index/index.component.html
Normal file
109
client/src/app/content/index/index.component.html
Normal file
@ -0,0 +1,109 @@
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h1>{{ "HOME.YourServers" | translate }}</h1>
|
||||
</div>
|
||||
<div class="col-md-auto d-flex align-self-center">
|
||||
<button class="btn btn-primary" (click)="openModal(addModalContent)">
|
||||
<fa-icon [icon]="icons.faAdd"></fa-icon> {{ "ADDEDITSERVER.AddNewServer" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p *ngIf="servers.length == 0">{{ "HOME.NoServersAdded" | translate }}</p>
|
||||
<div class="table-responsive" *ngIf="servers.length > 0">
|
||||
<table class="table table-striped table-hover text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ "GENERAL.Server" | translate }}</th>
|
||||
<th scope="col" class="d-none d-lg-table-cell">{{ "HOME.ServerUri" | translate }}</th>
|
||||
<th scope="col">{{ "HOME.Actions" | translate }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let server of servers; let i = index">
|
||||
<td>{{ server.serverName }}</td>
|
||||
<td class="d-none d-lg-table-cell" [class.blurry-text]="blurryUris">{{ server.serverURI }}</td>
|
||||
<td class="d-none d-lg-table-cell w-25">
|
||||
<button type="button" class="btn btn-primary me-1" [ngbTooltip]="'HOME.Connect' | translate" (click)="connectServer(server.serverName)" [ngClass]="isConnectedTo(server.serverName) ? 'btn-success' : 'btn-primary'">
|
||||
<fa-icon [icon]="icons.faTerminal"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" [ngbTooltip]="'HOME.MoveUp' | translate" (click)="moveServerUp(server.serverName)">
|
||||
<fa-icon [icon]="icons.faArrowUp"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" [ngbTooltip]="'HOME.MoveDown' | translate" (click)="moveServerDown(server.serverName)">
|
||||
<fa-icon [icon]="icons.faArrowDown"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" [ngbTooltip]="'HOME.Edit' | translate" (click)="serverClicked = server; openModal(editModalContent)" [disabled]="isConnectedTo(server.serverName)">
|
||||
<fa-icon [icon]="icons.faEdit"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" [ngbTooltip]="'HOME.Delete' | translate" (click)="deleteServer(server.serverName)">
|
||||
<fa-icon [icon]="icons.faTrashCan"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
<td class="d-lg-none w-25">
|
||||
<button type="button" class="btn btn-primary" (click)="serverClicked = server;openOffcanvas(serverDetailsOffcanvas)">
|
||||
<fa-icon [icon]="icons.faAnglesRight"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
<!-- td para móvil con un boton que saque offcanvas. El offcanvas movil sera el mismo de navegacion, mostrara menu y conexiones activas ¿y luego otro offcanvas para las acciones? -->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Modal template to add new server -->
|
||||
<ng-template #addModalContent let-modal>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{ "ADDEDITSERVER.AddNewServer" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="modal.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-add-server #addServer [asModal]="true"></app-add-server>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" (click)="addServer.saveServer(modal);">{{ "ADDEDITSERVER.Add" | translate }}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- Modal template to edit server -->
|
||||
<ng-template #editModalContent let-modal>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{ "ADDEDITSERVER.EditServer" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="modal.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-edit-server #editServer [asModal]="true" [serverNameBeingEdited]="serverClicked.serverName"></app-edit-server>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" (click)="editServer.saveServer(modal);">{{ "ADDEDITSERVER.EditServer" | translate }}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- Offcanvas template to show server details -->
|
||||
<ng-template #serverDetailsOffcanvas let-offcanvas>
|
||||
<div class="offcanvas-header">
|
||||
<h4 class="offcanvas-title" id="offcanvas-basic-title">{{ "HOME.ServerDetails" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="offcanvas.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<p><strong>{{ "GENERAL.Server" | translate }}:</strong> {{ serverClicked.serverName }}</p>
|
||||
<p><strong>{{ "HOME.ServerUri" | translate }}:</strong> {{ serverClicked.serverURI }}</p>
|
||||
<p><strong>{{ "HOME.Actions" | translate }}</strong></p>
|
||||
<p>
|
||||
<button type="button" class="btn btn-primary me-1" (click)="offcanvas.dismiss('Action'); connectServer(serverClicked.serverName)" [ngClass]="isConnectedTo(serverClicked.serverName) ? 'btn-success' : 'btn-primary'">
|
||||
<fa-icon [icon]="icons.faTerminal"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" (click)="offcanvas.dismiss('Action'); moveServerUp(serverClicked.serverName)">
|
||||
<fa-icon [icon]="icons.faArrowUp"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" (click)="offcanvas.dismiss('Action'); moveServerDown(serverClicked.serverName)">
|
||||
<fa-icon [icon]="icons.faArrowDown"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary me-1" (click)="offcanvas.dismiss('Action'); openModal(editModalContent)" [disabled]="isConnectedTo(serverClicked.serverName)">
|
||||
<fa-icon [icon]="icons.faEdit"></fa-icon>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" (click)="offcanvas.dismiss('Action'); deleteServer(serverClicked.serverName)">
|
||||
<fa-icon [icon]="icons.faTrashCan"></fa-icon>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</ng-template>
|
4
client/src/app/content/index/index.component.scss
Normal file
4
client/src/app/content/index/index.component.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.blurry-text {
|
||||
text-shadow: 0 0 8px black;
|
||||
color: transparent !important;
|
||||
}
|
25
client/src/app/content/index/index.component.spec.ts
Normal file
25
client/src/app/content/index/index.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { IndexComponent } from './index.component';
|
||||
|
||||
describe('IndexComponent', () => {
|
||||
let component: IndexComponent;
|
||||
let fixture: ComponentFixture<IndexComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ IndexComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IndexComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
91
client/src/app/content/index/index.component.ts
Normal file
91
client/src/app/content/index/index.component.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { Component, OnInit, TemplateRef } from '@angular/core';
|
||||
import { ServerDto } from 'src/app/_dto/ServerDto';
|
||||
import { SettingsEnum, StorageService } from 'src/app/_services/storage.service';
|
||||
import { Icons } from 'src/app/shared/icons';
|
||||
import { NgbModal, NgbOffcanvas } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Router } from '@angular/router';
|
||||
import { WebconsoleService } from 'src/app/_services/webconsole.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-index',
|
||||
templateUrl: './index.component.html',
|
||||
styleUrls: ['./index.component.scss']
|
||||
})
|
||||
export class IndexComponent implements OnInit {
|
||||
icons = Icons;
|
||||
blurryUris: boolean = true;
|
||||
servers: ServerDto[] = []; //List of servers
|
||||
currentlyConnectedServers: string[] = [];
|
||||
|
||||
//Helper properties
|
||||
serverClicked!: ServerDto; //When edit or details button is clicked, server is stored here to be able to send it to the modal or the offcasnvas
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private webConsoleService: WebconsoleService,
|
||||
private modalService: NgbModal,
|
||||
private offcanvasService: NgbOffcanvas,
|
||||
private router: Router,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.blurryUris = this.storageService.getSetting(SettingsEnum.BlurryUri);
|
||||
this.refreshServerList();
|
||||
|
||||
this.currentlyConnectedServers = this.webConsoleService.getCurrentConnectedServers();
|
||||
this.webConsoleService.getActiveConnectionsChangedSubject().subscribe({
|
||||
next: () => this.currentlyConnectedServers = this.webConsoleService.getCurrentConnectedServers()
|
||||
})
|
||||
}
|
||||
|
||||
refreshServerList(): void {
|
||||
this.servers = this.storageService.getAllServers();
|
||||
}
|
||||
|
||||
isConnectedTo(serverName: string): boolean {
|
||||
return this.currentlyConnectedServers.includes(serverName);
|
||||
}
|
||||
|
||||
openModal(modalContent: TemplateRef<any>) {
|
||||
this.modalService.open(modalContent, { size: 'lg' }).result.then((result) => {
|
||||
//Fullfilled
|
||||
this.refreshServerList();
|
||||
}, (reason) => {
|
||||
//Rejected
|
||||
this.refreshServerList();
|
||||
});
|
||||
}
|
||||
|
||||
openOffcanvas(offcanvasContent: TemplateRef<any>) {
|
||||
this.offcanvasService.open(offcanvasContent, { ariaLabelledBy: 'offcanvas-server-details', position: "end" }).result.then((result) => {
|
||||
//Closed
|
||||
|
||||
}, (reason) => {
|
||||
//Dismissed
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
connectServer(serverName: string): void {
|
||||
this.router.navigate(['console', serverName]);
|
||||
}
|
||||
|
||||
moveServerUp(serverName: string): void {
|
||||
const currentIndex: number = this.servers.findIndex(e => e.serverName == serverName);
|
||||
this.storageService.moveServerToIndex(serverName, currentIndex - 1);
|
||||
this.refreshServerList();
|
||||
}
|
||||
|
||||
moveServerDown(serverName: string): void {
|
||||
const currentIndex: number = this.servers.findIndex(e => e.serverName == serverName);
|
||||
this.storageService.moveServerToIndex(serverName, currentIndex + 1);
|
||||
this.refreshServerList();
|
||||
}
|
||||
|
||||
deleteServer(serverName: string): void {
|
||||
this.webConsoleService.closeConnection(serverName);
|
||||
this.storageService.deleteServer(serverName);
|
||||
this.refreshServerList();
|
||||
}
|
||||
|
||||
}
|
75
client/src/app/content/settings/settings.component.html
Normal file
75
client/src/app/content/settings/settings.component.html
Normal file
@ -0,0 +1,75 @@
|
||||
<h1>{{ "SETTINGS.WebConsoleClientSettings" | translate }}</h1>
|
||||
<h2>{{ "SETTINGS.GeneralSettings" | translate }}</h2>
|
||||
<div class="mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="showDateSettingsSwitch" [(ngModel)]="isDateSwitchChecked" (change)="onSwitchChanges()">
|
||||
<label class="form-check-label" for="showDateSettingsSwitch">{{ "SETTINGS.ShowTimeOnConsoleLine" | translate }}</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="readLogFileSwitch" [(ngModel)]="isLogFileSwitchChecked" (change)="onSwitchChanges()">
|
||||
<label class="form-check-label" for="readLogFileSwitch">{{ "SETTINGS.RetrieveFullLogOnConnect" | translate }}</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="showUriSwitch" [(ngModel)]="isBlurrySwitchChecked" (change)="onSwitchChanges()">
|
||||
<label class="form-check-label" for="showUriSwitch">{{ "SETTINGS.BlurryUriHomepage" | translate }}</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="widerSwitch" [(ngModel)]="isWiderSwitchChecked" (change)="onSwitchChanges()">
|
||||
<label class="form-check-label" for="widerSwitch">{{ "SETTINGS.WiderViewport" | translate }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>{{ "SETTINGS.MigrateData" | translate }}</h2>
|
||||
<p>{{ "SETTINGS.MigrateDataDescription" | translate }}</p>
|
||||
<div class="mb-2">
|
||||
<p>
|
||||
<button type="button" class="btn btn-outline-primary" (click)="openExportCollapse()" [attr.aria-expanded]="!exportContainerCollapsed" aria-controls="collapseExport">
|
||||
{{ "SETTINGS.ExportData" | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary ms-2" (click)="openImportCollapse();" [attr.aria-expanded]="!exportContainerCollapsed" aria-controls="collapseImport">
|
||||
{{ "SETTINGS.ImportData" | translate }}
|
||||
</button>
|
||||
<button *ngIf="!exportContainerCollapsed || !importContainerCollapsed" type="button" class="btn btn-outline-primary ms-2" (click)="closeMigrateCollapse();">
|
||||
<fa-icon [icon]="icons.faClose"></fa-icon>
|
||||
</button>
|
||||
</p>
|
||||
<div #collapseExport="ngbCollapse" [(ngbCollapse)]="exportContainerCollapsed">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-2 text-muted">{{ "SETTINGS.CopyString" | translate }}</h6>
|
||||
<p class="card-text">{{ exportString }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div #collapseImport="ngbCollapse" [(ngbCollapse)]="importContainerCollapsed">
|
||||
<div class="input-group mb-3" [class.is-valid]="errorOccuredImporting === true" [class.is-invalid]="errorOccuredImporting === false">
|
||||
<input type="text" class="form-control" placeholder="{{ 'SETTINGS.PasteString' | translate }}" aria-label="Paste import string..." aria-describedby="button-import" [(ngModel)]="importString">
|
||||
<button class="btn btn-outline-primary" type="button" id="button-import" (click)="onImportClick()">{{ "SETTINGS.Import" | translate }}</button>
|
||||
</div>
|
||||
<div class="valid-feedback">
|
||||
{{ "SETTINGS.ImportSuccessful" | translate }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ "SETTINGS.ImportFailed" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>{{ "SETTINGS.Language" | translate }}</h2>
|
||||
<p>{{ "SETTINGS.SelectLanguage" | translate }}</p>
|
||||
<div class="mb-2">
|
||||
<select [(ngModel)]="savedLanguage" (change)="onLanguageChanged()" class="form-select" aria-label="Language selector">
|
||||
<option value="en">English</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="no">Norsk</option>
|
||||
<option value="pl">Polski</option>
|
||||
<option value="ru">русский</option>
|
||||
<option value="zh">中文</option>
|
||||
</select>
|
||||
</div>
|
25
client/src/app/content/settings/settings.component.spec.ts
Normal file
25
client/src/app/content/settings/settings.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
79
client/src/app/content/settings/settings.component.ts
Normal file
79
client/src/app/content/settings/settings.component.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { SettingsEnum, StorageService } from 'src/app/_services/storage.service';
|
||||
import { Icons } from 'src/app/shared/icons';
|
||||
import { LanguageService } from 'src/app/_services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss']
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
icons = Icons;
|
||||
//Switch values
|
||||
isDateSwitchChecked!: boolean;
|
||||
isLogFileSwitchChecked!: boolean;
|
||||
isBlurrySwitchChecked!: boolean;
|
||||
isWiderSwitchChecked!: boolean;
|
||||
|
||||
//Export data
|
||||
exportContainerCollapsed: boolean = true;
|
||||
exportString: string = "";
|
||||
|
||||
//Import data
|
||||
importContainerCollapsed: boolean = true;
|
||||
importString: string = "";
|
||||
errorOccuredImporting: boolean | null = null;
|
||||
|
||||
//Language
|
||||
savedLanguage!: string;
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//Initialize switches
|
||||
this.isDateSwitchChecked = this.storageService.getSetting(SettingsEnum.DateTimePrefix);
|
||||
this.isLogFileSwitchChecked = this.storageService.getSetting(SettingsEnum.RetrieveLogFile);
|
||||
this.isBlurrySwitchChecked = this.storageService.getSetting(SettingsEnum.BlurryUri);
|
||||
this.isWiderSwitchChecked = this.storageService.getSetting(SettingsEnum.WiderViewport);
|
||||
|
||||
//Initialize language selector
|
||||
this.savedLanguage = this.storageService.getLanguage();
|
||||
}
|
||||
|
||||
onSwitchChanges(): void {
|
||||
this.storageService.setSetting(SettingsEnum.DateTimePrefix, this.isDateSwitchChecked);
|
||||
this.storageService.setSetting(SettingsEnum.RetrieveLogFile, this.isLogFileSwitchChecked);
|
||||
this.storageService.setSetting(SettingsEnum.BlurryUri, this.isBlurrySwitchChecked);
|
||||
this.storageService.setSetting(SettingsEnum.WiderViewport, this.isWiderSwitchChecked);
|
||||
}
|
||||
|
||||
openExportCollapse(): void {
|
||||
this.exportString = this.storageService.getExportString();
|
||||
this.exportContainerCollapsed = false;
|
||||
this.importContainerCollapsed = true;
|
||||
}
|
||||
|
||||
openImportCollapse(): void {
|
||||
this.exportContainerCollapsed = true;
|
||||
this.importContainerCollapsed = false;
|
||||
}
|
||||
|
||||
closeMigrateCollapse(): void {
|
||||
this.exportContainerCollapsed = true;
|
||||
this.importContainerCollapsed = true;
|
||||
}
|
||||
|
||||
onImportClick(): void {
|
||||
this.errorOccuredImporting = this.storageService.importSettings(this.importString);
|
||||
}
|
||||
|
||||
onLanguageChanged(): void {
|
||||
console.log(`Change language to ${this.savedLanguage}`)
|
||||
this.languageService.setLanguage(this.savedLanguage);
|
||||
}
|
||||
|
||||
}
|
19
client/src/app/core/core.module.ts
Normal file
19
client/src/app/core/core.module.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { LayoutComponent } from './layout/layout.component';
|
||||
import { ServerToolbarComponent } from './server-toolbar/server-toolbar.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
LayoutComponent,
|
||||
ServerToolbarComponent,
|
||||
],
|
||||
imports: [
|
||||
SharedModule
|
||||
],
|
||||
exports: [
|
||||
LayoutComponent,
|
||||
ServerToolbarComponent,
|
||||
]
|
||||
})
|
||||
export class CoreModule { }
|
45
client/src/app/core/language.module.ts
Normal file
45
client/src/app/core/language.module.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
|
||||
import { LanguageService } from "../_services/language.service";
|
||||
|
||||
export const HttpLoaderFactory = (http: HttpClient): TranslateHttpLoader => {
|
||||
return new TranslateHttpLoader(http, "./assets/i18n/")
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
})],
|
||||
exports: []
|
||||
})
|
||||
export class LanguageModule {
|
||||
readonly VALID_LANGUAGES = ["en", "es", "de", "fr", "no", "pl", "ru", "zh"];
|
||||
|
||||
constructor(private languageService: LanguageService) {
|
||||
this.setup();
|
||||
}
|
||||
|
||||
private setup(): void {
|
||||
//If a language is set in persistence, and it is supported by the app, use it
|
||||
const persistenceLanguage = this.languageService.getLanguage();
|
||||
|
||||
if (this.VALID_LANGUAGES.includes(persistenceLanguage)) {
|
||||
this.languageService.setLanguage(persistenceLanguage);
|
||||
} else {
|
||||
//If language is not defined in persistence, check browser default language: If browser uses a supported language, use it. Otherwise, use english.
|
||||
const browserLang = navigator.language.substring(0, 2);
|
||||
const defaultLang = this.VALID_LANGUAGES.includes(browserLang) ? browserLang : "en";
|
||||
|
||||
this.languageService.setLanguage(defaultLang);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
54
client/src/app/core/layout/layout.component.html
Normal file
54
client/src/app/core/layout/layout.component.html
Normal file
@ -0,0 +1,54 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" routerLink="/">
|
||||
<img src="assets/iconwhite.png" alt="Logo" width="30" height="30" class="d-inline-block align-text-top"> <span class="ms-3">WebConsole Client</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation" (click)="openOffcanvas(navbarOffcanvasContent)">
|
||||
<span class="navbar-toggler-icon" [class.rotated]="ifMobileNavigationOpen"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">{{ "LAYOUT.Home" | translate }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/settings" routerLinkActive="active">{{ "LAYOUT.Settings" | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<span class="navbar-text">v2.7 - </span>
|
||||
<a class="nav-link" target="_blank" href="https://github.com/mesacarlos/WebConsole">
|
||||
<fa-icon [icon]="icons.faArrowUpRightFromSquare"></fa-icon> GitHub
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div [ngClass]="{ 'container': !isWiderViewportEnabled, 'container-fluid': isWiderViewportEnabled }" class="mb-5">
|
||||
<!-- Content here -->
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
<app-server-toolbar></app-server-toolbar>
|
||||
|
||||
<ng-template #navbarOffcanvasContent let-offcanvas>
|
||||
<div class="offcanvas-header">
|
||||
<h4 class="offcanvas-title" id="offcanvas-basic-title">{{ "LAYOUT.Navigation" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="offcanvas.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<a class="nav-link" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }" (click)="offcanvas.dismiss('Navigate')">{{ "LAYOUT.Home" | translate }}</a>
|
||||
<a class="nav-link" routerLink="/settings" routerLinkActive="active" (click)="offcanvas.dismiss('Navigate')">{{ "LAYOUT.Settings" | translate }}</a>
|
||||
|
||||
<hr />
|
||||
<h4>{{ "LAYOUT.CurrentlyConnectedTo" | translate }}</h4>
|
||||
<p *ngIf="currentlyConnectedServers.length == 0">{{ "LAYOUT.NoConnectedToServers" | translate }}</p>
|
||||
<ul *ngIf="currentlyConnectedServers.length != 0" class="nav flex-column">
|
||||
<li class="nav-item" *ngFor="let server of currentlyConnectedServers">
|
||||
<a class="d-inline-block nav-link active" [routerLink]="['/console', server]" (click)="offcanvas.dismiss('Connect')">{{ server }}</a>
|
||||
<a class="d-inline-block nav-link active" (click)="disconnectServer(server)">({{ "LAYOUT.Disconnect" | translate }})</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-template>
|
7
client/src/app/core/layout/layout.component.scss
Normal file
7
client/src/app/core/layout/layout.component.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.navbar-toggler-icon {
|
||||
transition: transform 0.3s linear;
|
||||
}
|
||||
|
||||
.rotated {
|
||||
transform: rotate(-90deg);
|
||||
}
|
25
client/src/app/core/layout/layout.component.spec.ts
Normal file
25
client/src/app/core/layout/layout.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LayoutComponent } from './layout.component';
|
||||
|
||||
describe('LayoutComponent', () => {
|
||||
let component: LayoutComponent;
|
||||
let fixture: ComponentFixture<LayoutComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LayoutComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LayoutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
47
client/src/app/core/layout/layout.component.ts
Normal file
47
client/src/app/core/layout/layout.component.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Component, OnInit, TemplateRef } from '@angular/core';
|
||||
import { NgbOffcanvas } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Icons } from 'src/app/shared/icons';
|
||||
import { StorageService } from 'src/app/_services/storage.service';
|
||||
import { WebconsoleService } from 'src/app/_services/webconsole.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout',
|
||||
templateUrl: './layout.component.html',
|
||||
styleUrls: ['./layout.component.scss']
|
||||
})
|
||||
export class LayoutComponent implements OnInit {
|
||||
icons = Icons;
|
||||
currentlyConnectedServers: string[] = [];
|
||||
ifMobileNavigationOpen: boolean = false;
|
||||
isWiderViewportEnabled!: boolean;
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private webConsoleService: WebconsoleService,
|
||||
private offcanvasService: NgbOffcanvas,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.storageService.widerViewportSubject.subscribe(value => this.isWiderViewportEnabled = value);
|
||||
|
||||
this.webConsoleService.getActiveConnectionsChangedSubject().subscribe({
|
||||
next: () => this.currentlyConnectedServers = this.webConsoleService.getCurrentConnectedServers()
|
||||
});
|
||||
}
|
||||
|
||||
disconnectServer(serverName: string): void {
|
||||
this.webConsoleService.closeConnection(serverName);
|
||||
}
|
||||
|
||||
openOffcanvas(offcanvasContent: TemplateRef<any>) {
|
||||
this.ifMobileNavigationOpen = true;
|
||||
this.offcanvasService.open(offcanvasContent, { ariaLabelledBy: 'offcanvas-navigation' }).result.then((result) => {
|
||||
//Closed
|
||||
this.ifMobileNavigationOpen = false;
|
||||
}, (reason) => {
|
||||
//Dismissed
|
||||
this.ifMobileNavigationOpen = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<div class="toolbar d-none d-lg-block">
|
||||
<div *ngFor="let server of currentlyConnectedServers | slice:0:maxTabsToShow" class="toolbar-item ms-2 me-2 d-inline-flex">
|
||||
<span class="toolbar-item-description hover ps-3 pe-3 flex-grow-1 d-flex align-items-center" [routerLink]="['/console', server]" routerLinkActive="bg-primary">{{ server }}</span>
|
||||
<fa-icon class="toolbar-item-close hover d-flex align-items-center justify-content-center" [icon]="icons.faXmark" (click)="disconnectServer(server)"></fa-icon>
|
||||
</div>
|
||||
<div *ngIf="currentlyConnectedServers.length > maxTabsToShow" class="toolbar-item-all ms-2 me-2 d-inline-flex">
|
||||
<fa-icon class="toolbar-item-all-icon hover d-flex align-items-center justify-content-center" [icon]="icons.faAdd" (click)="openOffcanvas(navbarOffcanvasContent)"></fa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #navbarOffcanvasContent let-offcanvas>
|
||||
<div class="offcanvas-header">
|
||||
<h4 class="offcanvas-title" id="offcanvas-basic-title">{{ "LAYOUT.CurrentlyConnectedTo" | translate }}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="offcanvas.dismiss('Cross click')"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" *ngIf="currentlyConnectedServers.length == 0">
|
||||
<p>{{ "LAYOUT.NoConnectedToServers" | translate }}</p>
|
||||
</div>
|
||||
<div class="offcanvas-body" *ngIf="currentlyConnectedServers.length != 0">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item" *ngFor="let server of currentlyConnectedServers">
|
||||
<a class="d-inline-block nav-link active" [routerLink]="['/console', server]" (click)="offcanvas.dismiss('Connect')">{{ server }}</a>
|
||||
<a class="d-inline-block nav-link active" (click)="disconnectServer(server)">({{ "LAYOUT.Disconnect" | translate }})</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</ng-template>
|
@ -0,0 +1,47 @@
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.toolbar-item {
|
||||
width: 200px;
|
||||
background-color: var(--bs-dark);
|
||||
color: white;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.toolbar-item-description {
|
||||
height: 35px;
|
||||
border-radius: 10px 0 0 0;
|
||||
border-style: solid;
|
||||
border-color: white;
|
||||
border-width: 0 1px 0 0;
|
||||
}
|
||||
|
||||
.toolbar-item-close {
|
||||
height: 35px;
|
||||
width: 30px;
|
||||
border-radius: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.hover:hover {
|
||||
background-color: var(--bs-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toolbar-item-all {
|
||||
background-color: var(--bs-dark);
|
||||
color: white;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.toolbar-item-all-icon {
|
||||
height: 38px;
|
||||
width: 35px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ServerToolbarComponent } from './server-toolbar.component';
|
||||
|
||||
describe('ServerToolbarComponent', () => {
|
||||
let component: ServerToolbarComponent;
|
||||
let fixture: ComponentFixture<ServerToolbarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ServerToolbarComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ServerToolbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import { Component, HostListener, OnInit, TemplateRef } from '@angular/core';
|
||||
import { NgbOffcanvas } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Icons } from 'src/app/shared/icons';
|
||||
import { WebconsoleService } from 'src/app/_services/webconsole.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-server-toolbar',
|
||||
templateUrl: './server-toolbar.component.html',
|
||||
styleUrls: ['./server-toolbar.component.scss']
|
||||
})
|
||||
export class ServerToolbarComponent implements OnInit {
|
||||
icons = Icons;
|
||||
currentlyConnectedServers: string[] = [];
|
||||
maxTabsToShow: number = 0;
|
||||
|
||||
constructor(
|
||||
private webConsoleService: WebconsoleService,
|
||||
private offcanvasService: NgbOffcanvas,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.webConsoleService.getActiveConnectionsChangedSubject().subscribe({
|
||||
next: () => this.currentlyConnectedServers = this.webConsoleService.getCurrentConnectedServers()
|
||||
});
|
||||
this.onResize();
|
||||
}
|
||||
|
||||
disconnectServer(serverName: string): void {
|
||||
this.webConsoleService.closeConnection(serverName);
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(event?: any): void {
|
||||
const windowWidth: number = window.innerWidth;
|
||||
|
||||
//Each tab take aprox. 230px, with this info we can calculate how many tabs can we show at once
|
||||
this.maxTabsToShow = Math.floor(windowWidth / 240);
|
||||
}
|
||||
|
||||
openOffcanvas(offcanvasContent: TemplateRef<any>) {
|
||||
this.offcanvasService.open(offcanvasContent, { ariaLabelledBy: 'offcanvas-connected-servers', position: "end" });
|
||||
}
|
||||
|
||||
}
|
18
client/src/app/shared/icons.ts
Normal file
18
client/src/app/shared/icons.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { faAdd, faAnglesRight, faArrowDown, faArrowUp, faArrowUpRightFromSquare, faCircleXmark, faClose, faEdit, faEye, faEyeSlash, faLock, faTerminal, faTrashCan, faXmark, IconDefinition } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
export const Icons = {
|
||||
faAdd: faAdd,
|
||||
faClose: faClose,
|
||||
faTrashCan: faTrashCan,
|
||||
faArrowUp: faArrowUp,
|
||||
faArrowDown: faArrowDown,
|
||||
faEdit: faEdit,
|
||||
faTerminal: faTerminal,
|
||||
faArrowUpRightFromSquare: faArrowUpRightFromSquare,
|
||||
faAnglesRight: faAnglesRight,
|
||||
faEye: faEye,
|
||||
faEyeSlash: faEyeSlash,
|
||||
faCircleXmark: faCircleXmark,
|
||||
faXmark: faXmark,
|
||||
faLock: faLock,
|
||||
}
|
15
client/src/app/shared/sanitize.pipe.ts
Normal file
15
client/src/app/shared/sanitize.pipe.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({
|
||||
name: 'sanitize'
|
||||
})
|
||||
export class SanitizePipe implements PipeTransform {
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
||||
transform(html: string): SafeHtml {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(html);
|
||||
}
|
||||
|
||||
}
|
29
client/src/app/shared/shared.module.ts
Normal file
29
client/src/app/shared/shared.module.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { SanitizePipe } from './sanitize.pipe';
|
||||
|
||||
@NgModule({
|
||||
exports: [
|
||||
TranslateModule,
|
||||
FormsModule,
|
||||
FontAwesomeModule,
|
||||
NgbModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
CommonModule,
|
||||
SanitizePipe
|
||||
],
|
||||
declarations: [
|
||||
SanitizePipe
|
||||
],
|
||||
imports: [
|
||||
TranslateModule,
|
||||
CommonModule
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
0
client/src/assets/.gitkeep
Normal file
0
client/src/assets/.gitkeep
Normal file
86
client/src/assets/i18n/de.json
Normal file
86
client/src/assets/i18n/de.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Server",
|
||||
"Loading": "Laden..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Startseite",
|
||||
"Settings": "Einstellungen",
|
||||
"Navigation": "Navigation",
|
||||
"CurrentlyConnectedTo": "Zurzeit verbundene Server",
|
||||
"NoConnectedToServers": "Sie sind zurzeit mit keinem Server verbunden.",
|
||||
"Disconnect": "Trennen"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Ihre gespeicherten Server",
|
||||
"NoServersAdded": "Sie haben noch keine Server hinzugefügt. Fügen Sie Ihren ersten Server über die Schaltfläche oben rechts auf dieser Seite hinzu. Sehen Sie sich auch die Einstellungsseite an, um die Anpassung der WebConsole abzuschließen.",
|
||||
"ServerUri": "Server URI",
|
||||
"Actions": "Aktionen",
|
||||
"Connect": "Verbinden",
|
||||
"MoveUp": "Nach oben",
|
||||
"MoveDown": "Nach unten",
|
||||
"Edit": "Bearbeiten",
|
||||
"Delete": "Entfernen",
|
||||
"ServerDetails": "Server details"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "WebConsole Client Einstellungen",
|
||||
"GeneralSettings": "Allgemeine Einstellungen",
|
||||
"ShowTimeOnConsoleLine": "Uhrzeit auf jeder Konsolenzeile anzeigen",
|
||||
"RetrieveFullLogOnConnect": "Holt die vollständige Protokolldatei nach dem Verbindungsaufbau. Kann die Leistung für einige Sekunden beeinträchtigen oder den maximalen Browserspeicher erreichen, wenn eine Verbindung zu ausgelasteten Servern besteht.",
|
||||
"BlurryUriHomepage": "Unscharfe Server-URI auf der Startseite (Nützlich bei Verwendung von WebConsole in öffentlichen Bereichen)",
|
||||
"WiderViewport": "Breiteres Ansichtsfenster",
|
||||
"MigrateData": "Daten migrieren",
|
||||
"MigrateDataDescription": "Hier können Sie Ihre gespeicherten Server und Einstellungen exportieren und in einen anderen WebConsole Client importieren.",
|
||||
"ExportData": "Daten exportieren",
|
||||
"ImportData": "Daten importieren",
|
||||
"CopyString": "Kopieren Sie die folgende Zeichenfolge und fügen Sie sie in den gewünschten Client ein:",
|
||||
"PasteString": "Importzeichenfolge einfügen...",
|
||||
"Import": "Importieren",
|
||||
"ImportSuccessful": "Erfolgreich importiert!",
|
||||
"ImportFailed": "Beim Importieren ist ein Fehler aufgetreten. Überprüfen Sie Ihre exportierte Zeichenfolge und versuchen Sie es erneut.",
|
||||
"Language": "Sprache",
|
||||
"SelectLanguage": "Wählen Sie Ihre bevorzugte Sprache für die WebConsole aus."
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Neuen Server hinzufügen",
|
||||
"EditServer": "Server bearbeiten",
|
||||
"Name": "Name des Servers",
|
||||
"NamePlaceholder": "Mein server",
|
||||
"NameNotEditable": "Name nicht editierbar. Um ihn zu ändern, löschen Sie diesen Server und erstellen Sie ihn neu.",
|
||||
"Ip": "IP oder Domain",
|
||||
"IpPlaceholder": "192.168.0.1 oder mc.example.com",
|
||||
"Port": "Port",
|
||||
"Password": "Passwort (Optional)",
|
||||
"PasswordPlaceholder": "Leer lassen, um bei der Verbindung nach dem Passwort zu fragen.",
|
||||
"KeepPasswordUnchanged": "Passwort unverändert lassen",
|
||||
"SslEnabled": "SSL ist in der Plugin Konfiguration aktiviert",
|
||||
"SslEnabledMandatory": "SSL ist bei der Verwendung von Client über HTTPS-Verbindungen aufgrund von Browser Einschränkungen obligatorisch.",
|
||||
"Add": "Server hinzufügen",
|
||||
"RequiredField": "Dieses Feld ist erforderlich",
|
||||
"RequiredOrTooLongField": "Dieses Feld ist leer oder enthält mehr als 50 Zeichen",
|
||||
"InvalidPort": "Ungültiger Port",
|
||||
"ServerAlreadyExist": "Ein Server mit diesem Namen existiert bereits"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Serverinfo umschalten",
|
||||
"Connected": "Verbunden",
|
||||
"Disconnected": "Getrennt",
|
||||
"LoggedInAs": "Eingeloggt als",
|
||||
"PlayersOnline": "Spieler online",
|
||||
"CpuUsage": "CPU Auslastung",
|
||||
"RamUsage": "RAM Auslastung",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Anmeldung erforderlich. Zum Einloggen anklicken.",
|
||||
"Send": "Senden",
|
||||
"Connecting": "Verbindung wird hergestellt, bitte warten...",
|
||||
"CannotConnect": "Es kann keine Verbindung zum Server hergestellt werden.",
|
||||
"CannotConnectDescription1": "Vergewissern Sie sich, dass der Server läuft und dass der WebConsole Port sowohl in Ihrer Firewall als auch in Ihrem Router geöffnet ist. Sie können dies verwenden",
|
||||
"Tool": "Werkzeug",
|
||||
"CannotConnectDescription2": "um den Status Ihres Anschlusses zu überprüfen.",
|
||||
"PasswordRequested": "Passwort angefordert",
|
||||
"WrongPassword": "Falsches Passwort. Versuchen Sie es erneut.",
|
||||
"RememberPassword": "Passwort merken",
|
||||
"Connect": "Verbinden"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/en.json
Normal file
86
client/src/assets/i18n/en.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Server",
|
||||
"Loading": "Loading..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Home",
|
||||
"Settings": "Settings",
|
||||
"Navigation": "Navigation",
|
||||
"CurrentlyConnectedTo": "Currently connected servers",
|
||||
"NoConnectedToServers": "You are currently not connected to any server.",
|
||||
"Disconnect": "Disconnect"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Your saved servers",
|
||||
"NoServersAdded": "You have no servers added yet. Add your first one using the button on the top right of this page. Also, check out the settings page to finish customizing WebConsole.",
|
||||
"ServerUri": "Server URI",
|
||||
"Actions": "Actions",
|
||||
"Connect": "Connect",
|
||||
"MoveUp": "Move up",
|
||||
"MoveDown": "Move down",
|
||||
"Edit": "Edit",
|
||||
"Delete": "Delete",
|
||||
"ServerDetails": "Server details"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "WebConsole Client Settings",
|
||||
"GeneralSettings": "General Settings",
|
||||
"ShowTimeOnConsoleLine": "Show time on each console line",
|
||||
"RetrieveFullLogOnConnect": "Fetch full log file after connecting. May affect performance for a few seconds or reach maximum browser memory when connecting to busy servers.",
|
||||
"BlurryUriHomepage": "Blurry Server URI on homepage (Useful when using WebConsole in public spaces)",
|
||||
"WiderViewport": "Wider viewport",
|
||||
"MigrateData": "Migrate data",
|
||||
"MigrateDataDescription": "Here you can export your saved servers and settings and import them in another WebConsole Client.",
|
||||
"ExportData": "Export data",
|
||||
"ImportData": "Import data",
|
||||
"CopyString": "Copy the following string and paste it in your desired client:",
|
||||
"PasteString": "Paste import string...",
|
||||
"Import": "Import",
|
||||
"ImportSuccessful": "Imported successfully!",
|
||||
"ImportFailed": "Error occured importing. Check your exported string and try again.",
|
||||
"Language": "Language",
|
||||
"SelectLanguage": "Select your preferred language to use with WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Add new server",
|
||||
"EditServer": "Edit server",
|
||||
"Name": "Server name",
|
||||
"NamePlaceholder": "My server",
|
||||
"NameNotEditable": "Name not editable. To modify it, delete this server and create it again.",
|
||||
"Ip": "IP or Domain",
|
||||
"IpPlaceholder": "192.168.0.1 or mc.example.com",
|
||||
"Port": "Port",
|
||||
"Password": "Password (Optional)",
|
||||
"PasswordPlaceholder": "Leave blank to ask for password when connecting.",
|
||||
"KeepPasswordUnchanged": "Keep password unchanged",
|
||||
"SslEnabled": "SSL is enabled on the plugin config",
|
||||
"SslEnabledMandatory": "SSL is mandatory when using client over HTTPS connections due to browsers restrictions.",
|
||||
"Add": "Add server",
|
||||
"RequiredField": "This field is required",
|
||||
"RequiredOrTooLongField": "This field is empty or exceeds 50 characters",
|
||||
"InvalidPort": "Invalid port",
|
||||
"ServerAlreadyExist": "A server with this name already exists"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Toggle server info",
|
||||
"Connected": "Connected",
|
||||
"Disconnected": "Disconnected",
|
||||
"LoggedInAs": "Logged in as",
|
||||
"PlayersOnline": "Players online",
|
||||
"CpuUsage": "CPU Usage",
|
||||
"RamUsage": "RAM Usage",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Login required. Click to login.",
|
||||
"Send": "Send",
|
||||
"Connecting": "Connecting, please wait...",
|
||||
"CannotConnect": "Cannot connect to server.",
|
||||
"CannotConnectDescription1": "Please make sure that server is running, and that WebConsole port is open in both your firewall and router. You can use this",
|
||||
"Tool": "tool",
|
||||
"CannotConnectDescription2": "to verify your port status.",
|
||||
"PasswordRequested": "Password Requested",
|
||||
"WrongPassword": "Wrong password. Try again.",
|
||||
"RememberPassword": "Remember password",
|
||||
"Connect": "Connect"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/es.json
Normal file
86
client/src/assets/i18n/es.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Servidor",
|
||||
"Loading": "Cargando..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Inicio",
|
||||
"Settings": "Configuración",
|
||||
"Navigation": "Navegación",
|
||||
"CurrentlyConnectedTo": "Conexiones activas",
|
||||
"NoConnectedToServers": "Actualmente no estas conectado a ningún servidor.",
|
||||
"Disconnect": "Desconectar"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Tus servidores guardados",
|
||||
"NoServersAdded": "No has añadido ningún servidor aún. Comienza añadiendo uno utilizando el botón situado en la parte superior derecha de esta página. Ademas, puedes terminar de personalizar WebConsole utilizando la sección de configuración que encontrarás en el menú principal.",
|
||||
"ServerUri": "URI del servidor",
|
||||
"Actions": "Acciones",
|
||||
"Connect": "Conectar",
|
||||
"MoveUp": "Subir",
|
||||
"MoveDown": "Bajar",
|
||||
"Edit": "Editar",
|
||||
"Delete": "Borrar",
|
||||
"ServerDetails": "Detalles del servidor"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "Configuración del cliente WebConsole",
|
||||
"GeneralSettings": "Configuración general",
|
||||
"ShowTimeOnConsoleLine": "Mostrar tiempo en cada linea de consola",
|
||||
"RetrieveFullLogOnConnect": "Obtener log completo tras conectar al servidor. Puede afectar al rendimiento durante unos segundos o sobrepasar la memoria en servidores muy grandes.",
|
||||
"BlurryUriHomepage": "Obfuscar URI de servidor en página de inicio (Útil si usas WebConsole en espacios públicos)",
|
||||
"WiderViewport": "Ventana sin márgenes laterales",
|
||||
"MigrateData": "Migrar datos",
|
||||
"MigrateDataDescription": "A continuación puedes exportar tus servidores guardados e importarlos en otro cliente de WebConsole",
|
||||
"ExportData": "Exportar datos",
|
||||
"ImportData": "Importar datos",
|
||||
"CopyString": "Copia la siguiente cadena y pégala en el cliente WebConsole donde desees importar los datos:",
|
||||
"PasteString": "Pega tu cadena aquí...",
|
||||
"Import": "Importar",
|
||||
"ImportSuccessful": "Datos importados correctamente!",
|
||||
"ImportFailed": "Ocrrió un error al importar. Comprueba que la cadena exportada anteriormente sea correcta e inténtalo de nuevo.",
|
||||
"Language": "Idioma",
|
||||
"SelectLanguage": "Selecciona el idioma en el que mostrar el Cliente de WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Añadir nuevo servidor",
|
||||
"EditServer": "Editar servidor",
|
||||
"Name": "Nombre del servidor",
|
||||
"NamePlaceholder": "Mi server",
|
||||
"NameNotEditable": "Nombre no editable. Si deseas modificarlo, debes eliminar el servidor y crearlo de nuevo.",
|
||||
"Ip": "IP o Dominio",
|
||||
"IpPlaceholder": "192.168.0.1 o mc.example.com",
|
||||
"Port": "Puerto",
|
||||
"Password": "Contraseña (Opcional)",
|
||||
"PasswordPlaceholder": "Deja este campo en blanco para introducir la contraseña al conectar.",
|
||||
"KeepPasswordUnchanged": "No modificar contraseña",
|
||||
"SslEnabled": "SSL activado en la configuración del plugin",
|
||||
"SslEnabledMandatory": "SSL es obligatorio cuando se utiliza el cliente bajo HTTPS debido a restricciones de los navegadores.",
|
||||
"Add": "Añadir servidor",
|
||||
"RequiredField": "Este campo es obligatorio",
|
||||
"RequiredOrTooLongField": "Este campo está vacio o sobrepasa los 50 caracteres",
|
||||
"InvalidPort": "Puerto inválido",
|
||||
"ServerAlreadyExist": "Ya existe un servidor con el mismo nombre"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Mostrar/Ocultar información del servidor",
|
||||
"Connected": "Conectado",
|
||||
"Disconnected": "Desconectado",
|
||||
"LoggedInAs": "Sesión iniciada como",
|
||||
"PlayersOnline": "Jugadores conectados",
|
||||
"CpuUsage": "Uso de CPU",
|
||||
"RamUsage": "Uso de RAM",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Inicio de sesión requerido. Click para iniciar sesión.",
|
||||
"Send": "Enviar",
|
||||
"Connecting": "Conectando, espere...",
|
||||
"CannotConnect": "No se puede conectar con el servidor.",
|
||||
"CannotConnectDescription1": "Asegúrate que el servidor está en ejecución, y que el puerto de WebConsole está abierto en tu firewall y/o router. Puedes usar esta",
|
||||
"Tool": "herramienta",
|
||||
"CannotConnectDescription2": "para comprobar si tu puerto está abierto.",
|
||||
"PasswordRequested": "Contraseña necesaria",
|
||||
"WrongPassword": "Contraseña incorrecta. Inténtelo de nuevo.",
|
||||
"RememberPassword": "Recordar contraseña",
|
||||
"Connect": "Conectar"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/fr.json
Normal file
86
client/src/assets/i18n/fr.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Serveur",
|
||||
"Loading": "Chargement en cours..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Accueil",
|
||||
"Settings": "Paramètres",
|
||||
"Navigation": "Navigation",
|
||||
"CurrentlyConnectedTo": "Actuellement connecté aux serveurs",
|
||||
"NoConnectedToServers": "Vous n'êtes actuellement connecté à aucun serveur.",
|
||||
"Disconnect": "Déconnexion"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Vos serveurs enregistrés",
|
||||
"NoServersAdded": "Vous n'avez encore ajouté aucun serveur. Ajoutez le premier en utilisant le bouton en haut à droite de cette page. De plus, consultez la page des paramètres pour personnaliser davantage WebConsole.",
|
||||
"ServerUri": "URI du serveur",
|
||||
"Actions": "Actions",
|
||||
"Connect": "Connecter",
|
||||
"MoveUp": "Déplacer vers le haut",
|
||||
"MoveDown": "Déplacer vers le bas",
|
||||
"Edit": "Modifier",
|
||||
"Delete": "Supprimer",
|
||||
"ServerDetails": "Détails du serveur"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "Paramètres du client WebConsole",
|
||||
"GeneralSettings": "Paramètres généraux",
|
||||
"ShowTimeOnConsoleLine": "Afficher l'heure sur chaque ligne de console",
|
||||
"RetrieveFullLogOnConnect": "Récupérer le fichier journal complet après la connexion. Peut affecter les performances pendant quelques secondes ou atteindre la mémoire maximale du navigateur lors de la connexion à des serveurs occupés.",
|
||||
"BlurryUriHomepage": "URI du serveur flou sur la page d'accueil (utile lorsque vous utilisez WebConsole dans des espaces publics)",
|
||||
"WiderViewport": "Vue plus large",
|
||||
"MigrateData": "Migrer les données",
|
||||
"MigrateDataDescription": "Ici, vous pouvez exporter vos serveurs enregistrés et vos paramètres et les importer dans un autre client WebConsole.",
|
||||
"ExportData": "Exporter les données",
|
||||
"ImportData": "Importer les données",
|
||||
"CopyString": "Copiez la chaîne suivante et collez-la dans votre client désiré :",
|
||||
"PasteString": "Coller la chaîne d'import...",
|
||||
"Import": "Importer",
|
||||
"ImportSuccessful": "Importation réussie !",
|
||||
"ImportFailed": "Erreur lors de l'importation. Vérifiez votre chaîne exportée et réessayez.",
|
||||
"Language": "Langue",
|
||||
"SelectLanguage": "Sélectionnez votre langue préférée à utiliser avec WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Ajouter un nouveau serveur",
|
||||
"EditServer": "Modifier le serveur",
|
||||
"Name": "Nom du serveur",
|
||||
"NamePlaceholder": "Mon serveur",
|
||||
"NameNotEditable": "Le nom n'est pas modifiable. Pour le modifier, supprimez ce serveur et recréez-le.",
|
||||
"Ip": "IP ou domaine",
|
||||
"IpPlaceholder": "192.168.0.1 ou mc.example.com",
|
||||
"Port": "Port",
|
||||
"Password": "Mot de passe (facultatif)",
|
||||
"PasswordPlaceholder": "Laissez vide pour demander un mot de passe lors de la connexion.",
|
||||
"KeepPasswordUnchanged": "Garder le mot de passe inchangé",
|
||||
"SslEnabled": "SSL est activé dans la configuration du plugin",
|
||||
"SslEnabledMandatory": "SSL est obligatoire lors de l'utilisation du client via des connexions HTTPS en raison des restrictions des navigateurs.",
|
||||
"Add": "Ajouter le serveur",
|
||||
"RequiredField": "Ce champ est obligatoire",
|
||||
"RequiredOrTooLongField": "Ce champ est vide ou dépasse 50 caractères",
|
||||
"InvalidPort": "Port invalide",
|
||||
"ServerAlreadyExist": "Un serveur avec ce nom existe déjà"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Basculer les informations du serveur",
|
||||
"Connected": "Connecté",
|
||||
"Disconnected": "Déconnecté",
|
||||
"LoggedInAs": "Connecté en tant que",
|
||||
"PlayersOnline": "Joueurs en ligne",
|
||||
"CpuUsage": "Utilisation du CPU",
|
||||
"RamUsage": "Utilisation de la RAM",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Connexion requise. Cliquez pour vous connecter.",
|
||||
"Send": "Envoyer",
|
||||
"Connecting": "Connexion en cours, veuillez patienter...",
|
||||
"CannotConnect": "Impossible de se connecter au serveur.",
|
||||
"CannotConnectDescription1": "Assurez-vous que le serveur est en cours d'exécution et que le port WebConsole est ouvert à la fois dans votre pare-feu et votre routeur. Vous pouvez utiliser cet",
|
||||
"Tool": "outil",
|
||||
"CannotConnectDescription2": "pour vérifier l'état de votre port.",
|
||||
"PasswordRequested": "Mot de passe requis",
|
||||
"WrongPassword": "Mot de passe incorrect. Réessayez.",
|
||||
"RememberPassword": "Mémoriser le mot de passe",
|
||||
"Connect": "Connecter"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/no.json
Normal file
86
client/src/assets/i18n/no.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Server",
|
||||
"Loading": "Laster..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Hjem",
|
||||
"Settings": "Innstillinger",
|
||||
"Navigation": "Navigasjon",
|
||||
"CurrentlyConnectedTo": "Tilkoblede servere",
|
||||
"NoConnectedToServers": "Du er for øyeblikket ikke koblet til noen server.",
|
||||
"Disconnect": "Koblet fra"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Dine lagrede servere",
|
||||
"NoServersAdded": "Du har ingen servere lagt til ennå. Legg til den første ved hjelp av knappen øverst til høyre på denne siden. Sjekk også ut innstillingene for å fullføre tilpasningen av WebConsole.",
|
||||
"ServerUri": "Server URI",
|
||||
"Actions": "Handlinger",
|
||||
"Connect": "Koble til",
|
||||
"MoveUp": "Beveg opp",
|
||||
"MoveDown": "Beveg ned",
|
||||
"Edit": "Rediger",
|
||||
"Delete": "Slett",
|
||||
"ServerDetails": "Server detaljer"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "Innstillinger for WebConsole-klient",
|
||||
"GeneralSettings": "Generelle Innstillinger",
|
||||
"ShowTimeOnConsoleLine": "Vis tid på hver konsolllinje",
|
||||
"RetrieveFullLogOnConnect": "Hent full loggfil etter tilkobling. Kan påvirke ytelsen i noen sekunder eller nå maksimalt nettleserminne når du kobler til travle servere.",
|
||||
"BlurryUriHomepage": "Blurr server-URI på hjemmesiden (nyttig når du bruker WebConsole på offentlige steder)",
|
||||
"WiderViewport": "Bredere visningsport",
|
||||
"MigrateData": "Overfør data",
|
||||
"MigrateDataDescription": "Her kan du eksportere dine lagrede servere og innstillinger og importere dem i en annen WebConsole-klient.",
|
||||
"ExportData": "Eksporter data",
|
||||
"ImportData": "Importer data",
|
||||
"CopyString": "Kopier følgende texst og lim den inn i ønsket klient:",
|
||||
"PasteString": "Lim inn importtekst...",
|
||||
"Import": "Importer",
|
||||
"ImportSuccessful": "Importering vellykket!",
|
||||
"ImportFailed": "Det oppstod en feil under importering. Sjekk den eksporterte teksten, og prøv på nytt.",
|
||||
"Language": "Språk",
|
||||
"SelectLanguage": "Velg foretrukket språk å bruke med WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Legg til ny server",
|
||||
"EditServer": "Rediger server",
|
||||
"Name": "Server navn",
|
||||
"NamePlaceholder": "Min server",
|
||||
"NameNotEditable": "Navnet kan ikke redigeres. For å endre navnet, slett denne serveren og opprett den på nytt.",
|
||||
"Ip": "IP eller Domene",
|
||||
"IpPlaceholder": "192.168.0.1 eller mc.eksempel.no",
|
||||
"Port": "Port",
|
||||
"Password": "Passord (valgfritt)",
|
||||
"PasswordPlaceholder": "La stå tomt for å be om passord når du kobler til.",
|
||||
"KeepPasswordUnchanged": "Hold passordet uendret",
|
||||
"SslEnabled": "SSL er aktivert i config.yml",
|
||||
"SslEnabledMandatory": "SSL er obligatorisk når du bruker klient over HTTPS-tilkoblinger på grunn av nettleserbegrensninger.",
|
||||
"Add": "Legg til server",
|
||||
"RequiredField": "Dette feltet er obligatorisk",
|
||||
"RequiredOrTooLongField": "Dette feltet er tomt eller overskrider 50 tegn",
|
||||
"InvalidPort": "Ugyldig port",
|
||||
"ServerAlreadyExist": "En server med dette navnet finnes allerede"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Hvis/skjul serverinformasjon",
|
||||
"Connected": "Tilkoblet",
|
||||
"Disconnected": "Frakoblet",
|
||||
"LoggedInAs": "Logget inn som",
|
||||
"PlayersOnline": "Spillere online",
|
||||
"CpuUsage": "CPU bruk",
|
||||
"RamUsage": "RAM bruk",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Innlogging kreves. Klikk for å logge inn.",
|
||||
"Send": "Send",
|
||||
"Connecting": "Kobler til, vennligst vent...",
|
||||
"CannotConnect": "Kan ikke koble til serveren.",
|
||||
"CannotConnectDescription1": "Kontroller at serveren kjører, og at WebConsole-porten er åpen i både brannmuren og ruteren. Du kan bruke dette",
|
||||
"Tool": "verktøyet",
|
||||
"CannotConnectDescription2": "for å bekrefte portstatusen.",
|
||||
"PasswordRequested": "Passord forespurt",
|
||||
"WrongPassword": "Feil passord. Prøv igjen.",
|
||||
"RememberPassword": "Husk passord",
|
||||
"Connect": "Koble til"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/pl.json
Normal file
86
client/src/assets/i18n/pl.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Serwer",
|
||||
"Loading": "Wczytywanie..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Strona główna",
|
||||
"Settings": "Ustawienia",
|
||||
"Navigation": "Nawigacja",
|
||||
"CurrentlyConnectedTo": "Aktualnie połączone serwery",
|
||||
"NoConnectedToServers": "Nie jesteś aktualnie połączony z żadnym serwerem.",
|
||||
"Disconnect": "Rozłącz"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Twoje zapisane serwery",
|
||||
"NoServersAdded": "Nie masz jeszcze żadnych zapisanych serwerów. Dodaj swój pierwszy serwer za pomocą przycisku w prawym górnym rogu tej strony. Poza tym, zajrzyj do ustawień aby ukończyć personalizowanie WebConsole.",
|
||||
"ServerUri": "URI Serwera",
|
||||
"Actions": "Akcje",
|
||||
"Connect": "Połącz",
|
||||
"MoveUp": "Przenieś w górę",
|
||||
"MoveDown": "Przenieś w dół",
|
||||
"Edit": "Edytuj",
|
||||
"Delete": "Usuń",
|
||||
"ServerDetails": "Właściwości serwera"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "Ustawienia klienta WebConsole",
|
||||
"GeneralSettings": "Główne Ustawienia",
|
||||
"ShowTimeOnConsoleLine": "Pokazuj czas na każdej linijce w konsoli",
|
||||
"RetrieveFullLogOnConnect": "Zaciągnij pełny plik logu po połączeniu. Może wpłynąć na wydajność na kilka sekund albo osiągnąć maksymalne zużycie pamięci przeglądarki przy łączeniu do bardzo aktywnych serwerów.",
|
||||
"BlurryUriHomepage": "Rozmyty URI Serwera na stronie głównej (Przydatny w wypadku użycia WebConsole w miejscach publicznych)",
|
||||
"WiderViewport": "Szerszy podgląd",
|
||||
"MigrateData": "Migruj dane",
|
||||
"MigrateDataDescription": "Tutaj możesz wyeksportować swoje zapisane serwery i ustawienia i zaimportować je w innym kliencie WebConsole.",
|
||||
"ExportData": "Eksportuj dane",
|
||||
"ImportData": "Importuj dane",
|
||||
"CopyString": "Kopiuj następujący ciąg znaków i wklej go w twoim preferowanym kliencie:",
|
||||
"PasteString": "Wklej ciąg znaków do zaimportowania...",
|
||||
"Import": "Zaimportuj",
|
||||
"ImportSuccessful": "Zaimportowano z powodzeniem!",
|
||||
"ImportFailed": "Wystąpił błąd przy importowaniu. Sprawdź swój eksportowany ciąg znaków i spróbuj ponownie.",
|
||||
"Language": "Język",
|
||||
"SelectLanguage": "Wybierz swój preferowany język do użycia w WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Dodaj nowy serwer",
|
||||
"EditServer": "Edytuj serwer",
|
||||
"Name": "Nazwa serwera",
|
||||
"NamePlaceholder": "Mój serwer",
|
||||
"NameNotEditable": "Nazwy nie można edytować. Aby ją zmienić, usuń ten serwer i dodaj go spowrotem.",
|
||||
"Ip": "IP lub Domena",
|
||||
"IpPlaceholder": "192.168.0.1 lub mc.example.com",
|
||||
"Port": "Port",
|
||||
"Password": "Hasło (Opcjonalne)",
|
||||
"PasswordPlaceholder": "Pozostaw puste żeby pytać o hasło przy łączeniu.",
|
||||
"KeepPasswordUnchanged": "Pozostaw hasło nie zmienione",
|
||||
"SslEnabled": "SSL jest włączone w konfiguracji pluginu",
|
||||
"SslEnabledMandatory": "SSL jest obowiązkowe jeśli używasz klienta poprzez połączenia HTTPS z racji ograniczeń przeglądarek.",
|
||||
"Add": "Dodaj serwer",
|
||||
"RequiredField": "To pole jest wymagane",
|
||||
"RequiredOrTooLongField": "To pole jest puste lub przekracza 50 znaków",
|
||||
"InvalidPort": "Nieprawidłowy port",
|
||||
"ServerAlreadyExist": "Serwer z tą samą nazwą już istnieje"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Przełącz informacje o serwerze",
|
||||
"Connected": "Połączony",
|
||||
"Disconnected": "Rozłączony",
|
||||
"LoggedInAs": "Zalogowano jako",
|
||||
"PlayersOnline": "Gracze online",
|
||||
"CpuUsage": "Zużycie CPU",
|
||||
"RamUsage": "Zużycie RAM-u",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Logowanie wymagane. Kliknij aby się zalogować.",
|
||||
"Send": "Wyślij",
|
||||
"Connecting": "Łączenie, proszę czekać...",
|
||||
"CannotConnect": "Nie można połączyć z serwerem.",
|
||||
"CannotConnectDescription1": "Upewnij się że serwer pracuje, i że port WebConsole jest otwarty w twojej zaporze oraz routerze. Możesz użyć tego",
|
||||
"Tool": "narzędzia",
|
||||
"CannotConnectDescription2": "żeby zweryfikować status swoich portów.",
|
||||
"PasswordRequested": "Zażądano hasła",
|
||||
"WrongPassword": "Nieprawidłowe hasło. Spróbuj ponownie.",
|
||||
"RememberPassword": "Zapamiętaj hasło",
|
||||
"Connect": "Połącz"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/ru.json
Normal file
86
client/src/assets/i18n/ru.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "Сервер",
|
||||
"Loading": "Загрузка..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "Главная",
|
||||
"Settings": "Настройки",
|
||||
"Navigation": "Навигация",
|
||||
"CurrentlyConnectedTo": "Подключенные серверы",
|
||||
"NoConnectedToServers": "Вы не подключены ни к одному серверу.",
|
||||
"Disconnect": "Отключить"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "Ваши сохраненные серверы",
|
||||
"NoServersAdded": "У вас еще нет добавленных серверов. Добавьте свой первый, используя кнопку в правом верхнем углу этой страницы. Кроме того, посетите страницу настроек, чтобы завершить настройку WebConsole.",
|
||||
"ServerUri": "URI-адрес сервера",
|
||||
"Actions": "Действия",
|
||||
"Connect": "Подключить",
|
||||
"MoveUp": "Вверх",
|
||||
"MoveDown": "Вниз",
|
||||
"Edit": "Редактировать",
|
||||
"Delete": "Удалить",
|
||||
"ServerDetails": "Сведения о сервере"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "Настройки клиента WebConsole",
|
||||
"GeneralSettings": "Общие настройки",
|
||||
"ShowTimeOnConsoleLine": "Показывать время в каждой строке консоли",
|
||||
"RetrieveFullLogOnConnect": "Получить полный файл журнала после подключения. Может повлиять на производительность на несколько секунд или достичь максимального объема памяти браузера при подключении к загруженным серверам.",
|
||||
"BlurryUriHomepage": "Blurry Server URI на домашней странице (полезно при использовании WebConsole в общественных местах)",
|
||||
"WiderViewport": "Более широкое окно просмотра",
|
||||
"MigrateData": "Перенести данные",
|
||||
"MigrateDataDescription": "Здесь вы можете экспортировать сохраненные серверы и настройки и импортировать их в другой клиент WebConsole.",
|
||||
"ExportData": "Экспорт данных",
|
||||
"ImportData": "Импорт данных",
|
||||
"CopyString": "Скопируйте следующую строку и вставьте ее в нужный клиент:",
|
||||
"PasteString": "Вставить строку импорта...",
|
||||
"Import": "Импорт",
|
||||
"ImportSuccessful": "Импортирован успешно!",
|
||||
"ImportFailed": "Произошла ошибка импорта. Проверьте экспортированную строку и повторите попытку.",
|
||||
"Language": "Язык",
|
||||
"SelectLanguage": "Выберите предпочитаемый язык для использования с WebConsole"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "Добавить новый сервер",
|
||||
"EditServer": "Изменить сервер",
|
||||
"Name": "Название сервера",
|
||||
"NamePlaceholder": "Мой сервер",
|
||||
"NameNotEditable": "Имя не редактируется. Чтобы изменить его, удалите этот сервер и создайте его снова.",
|
||||
"Ip": "IP или домен",
|
||||
"IpPlaceholder": "192.168.0.1 or mc.example.com",
|
||||
"Port": "Порт",
|
||||
"Password": "Пароль (По желанию)",
|
||||
"PasswordPlaceholder": "Оставьте пустым, чтобы запрашивать пароль при подключении.",
|
||||
"KeepPasswordUnchanged": "Сохранить пароль без изменений",
|
||||
"SslEnabled": "SSL включен в конфигурации плагина",
|
||||
"SslEnabledMandatory": "SSL является обязательным при использовании клиента через соединения HTTPS из-за ограничений браузеров.",
|
||||
"Add": "Добавить сервер",
|
||||
"RequiredField": "Это поле обязательно к заполнению",
|
||||
"RequiredOrTooLongField": "Это поле пусто или превышает 50 символов",
|
||||
"InvalidPort": "Неверный порт",
|
||||
"ServerAlreadyExist": "Сервер с таким именем уже существует"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "Переключить информацию о сервере",
|
||||
"Connected": "Подключено",
|
||||
"Disconnected": "Отключено",
|
||||
"LoggedInAs": "Вы вошли как",
|
||||
"PlayersOnline": "Игроки онлайн",
|
||||
"CpuUsage": "Использование процессора",
|
||||
"RamUsage": "Использование памяти",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "Требуется авторизация. Нажмите, чтобы войти.",
|
||||
"Send": "Отправить",
|
||||
"Connecting": "Подключение, пожалуйста, подождите...",
|
||||
"CannotConnect": "Нет соединения с сервером.",
|
||||
"CannotConnectDescription1": "Убедитесь, что сервер работает, а порт WebConsole открыт как в брандмауэре, так и в маршрутизаторе. Вы можете использовать этот",
|
||||
"Tool": "инструмент",
|
||||
"CannotConnectDescription2": "чтобы проверить статус вашего порта.",
|
||||
"PasswordRequested": "Запрашиваемый пароль",
|
||||
"WrongPassword": "Неправильный пароль. Попробуйте еще раз.",
|
||||
"RememberPassword": "Запомнить пароль",
|
||||
"Connect": "Подключить"
|
||||
}
|
||||
}
|
86
client/src/assets/i18n/zh.json
Normal file
86
client/src/assets/i18n/zh.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"GENERAL": {
|
||||
"Server": "服务器",
|
||||
"Loading": "正在加载..."
|
||||
},
|
||||
"LAYOUT": {
|
||||
"Home": "主页面",
|
||||
"Settings": "设置",
|
||||
"Navigation": "导航",
|
||||
"CurrentlyConnectedTo": "当前连接的服务器",
|
||||
"NoConnectedToServers": "您当前未连接到任何服务器。",
|
||||
"Disconnect": "断开连接"
|
||||
},
|
||||
"HOME": {
|
||||
"YourServers": "服务器列表",
|
||||
"NoServersAdded": "您还没有添加任何服务器。使用此页面右上角的按钮添加您的第一个。此外,请查看设置页面以完成自定义 WebConsole。",
|
||||
"ServerUri": "服务器地址",
|
||||
"Actions": "功能",
|
||||
"Connect": "连接服务器",
|
||||
"MoveUp": "上移",
|
||||
"MoveDown": "下移",
|
||||
"Edit": "编辑服务器",
|
||||
"Delete": "删除服务器",
|
||||
"ServerDetails": "详细信息"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"WebConsoleClientSettings": "WebConsole 客户端设置",
|
||||
"GeneralSettings": "通用设置",
|
||||
"ShowTimeOnConsoleLine": "在每行日志上显示时间",
|
||||
"RetrieveFullLogOnConnect": "连接后加载完整的日志,若服务器有很多日志,这可能会影响性能或占用过多内存",
|
||||
"BlurryUriHomepage": "在主页面隐藏服务器地址 (在公共场所使用 WebConsole 时很有用) ",
|
||||
"WiderViewport": "更宽的窗口",
|
||||
"MigrateData": "迁移数据",
|
||||
"MigrateDataDescription": "您可以在此处导出保存的服务器和设置,并将它们导入另一个 WebConsole 客户端。",
|
||||
"ExportData": "导出数据",
|
||||
"ImportData": "导入数据",
|
||||
"CopyString": "复制以下字符串并将其粘贴到所需的客户端中: ",
|
||||
"PasteString": "粘贴导入字符串...",
|
||||
"Import": "确定",
|
||||
"ImportSuccessful": "导入成功! ",
|
||||
"ImportFailed": "导入时发生错误。检查您导出的字符串,然后重试。",
|
||||
"Language": "语言",
|
||||
"SelectLanguage": "选择 WebConsole 客户端的语言。"
|
||||
},
|
||||
"ADDEDITSERVER": {
|
||||
"AddNewServer": "添加服务器",
|
||||
"EditServer": "编辑服务器",
|
||||
"Name": "服务器名称",
|
||||
"NamePlaceholder": "My server",
|
||||
"NameNotEditable": "名称不可编辑。要修改它,请删除此服务器并重新创建它。",
|
||||
"Ip": "服务器地址",
|
||||
"IpPlaceholder": "192.168.0.1 或 mc.example.com",
|
||||
"Port": "端口",
|
||||
"Password": "密码 (可选) ",
|
||||
"PasswordPlaceholder": "留空以在连接时要求输入密码",
|
||||
"KeepPasswordUnchanged": "保持密码不变",
|
||||
"SslEnabled": "在插件配置上启用 SSL",
|
||||
"SslEnabledMandatory": "由于浏览器限制,在通过 HTTPS 连接的客户端上必须启用 SSL。",
|
||||
"Add": "添加服务器",
|
||||
"RequiredField": "这是必填项目",
|
||||
"RequiredOrTooLongField": "此字段为空或超过 50 个字符",
|
||||
"InvalidPort": "端口无效",
|
||||
"ServerAlreadyExist": "已存在同名服务器"
|
||||
},
|
||||
"CONSOLE": {
|
||||
"ToggleServerInfo": "切换服务器信息",
|
||||
"Connected": "已连接",
|
||||
"Disconnected": "断开连接",
|
||||
"LoggedInAs": "登录为",
|
||||
"PlayersOnline": "在线玩家",
|
||||
"CpuUsage": "CPU",
|
||||
"RamUsage": "RAM",
|
||||
"Tps": "TPS",
|
||||
"ClickToLogin": "要求登录。点击登录。",
|
||||
"Send": "发送",
|
||||
"Connecting": "正在连接中,请稍候...",
|
||||
"CannotConnect": "无法连接到服务器。",
|
||||
"CannotConnectDescription1": "请确保服务器正在运行,并且 WebConsole 端口在您的防火墙和路由器中均已打开。可以使用 ",
|
||||
"Tool": "此工具",
|
||||
"CannotConnectDescription2": " 验证端口状态。",
|
||||
"PasswordRequested": "要求密码",
|
||||
"WrongPassword": "密码错误。再试一次。",
|
||||
"RememberPassword": "记住密码",
|
||||
"Connect": "连接"
|
||||
}
|
||||
}
|
BIN
client/src/assets/icon.png
Normal file
BIN
client/src/assets/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 885 B |
BIN
client/src/assets/iconwhite.png
Normal file
BIN
client/src/assets/iconwhite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 898 B |
3
client/src/environments/environment.prod.ts
Normal file
3
client/src/environments/environment.prod.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
16
client/src/environments/environment.ts
Normal file
16
client/src/environments/environment.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
BIN
client/src/favicon.ico
Normal file
BIN
client/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
13
client/src/index.html
Normal file
13
client/src/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>WebConsole Client</title>
|
||||
<base href="./">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
12
client/src/main.ts
Normal file
12
client/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
57
client/src/polyfills.ts
Normal file
57
client/src/polyfills.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/***************************************************************************************************
|
||||
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
|
||||
*/
|
||||
import '@angular/localize/init';
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes recent versions of Safari, Chrome (including
|
||||
* Opera), Edge on the desktop, and iOS and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js'; // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user