mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2025-01-18 17:15:13 +01:00
Merge branch 'dev' into 'freddy0-Unraid-Template'
# Conflicts: # docker/unraid.xml
This commit is contained in:
commit
6637748a59
@ -6,6 +6,7 @@ docker-compose.yml
|
|||||||
|
|
||||||
# git & gitlab related
|
# git & gitlab related
|
||||||
.git/
|
.git/
|
||||||
|
.gitlab/
|
||||||
.gitignore
|
.gitignore
|
||||||
.gitlab-ci.yml
|
.gitlab-ci.yml
|
||||||
|
|
||||||
|
@ -2,15 +2,25 @@
|
|||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*.{js,py,html}]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
# end_of_line = lf
|
|
||||||
|
|
||||||
[*.py]
|
[*.py]
|
||||||
indent_style = space
|
profile = black
|
||||||
indent_size = 4
|
# > Handled by Black
|
||||||
|
# indent_style = space
|
||||||
|
# indent_size = 4
|
||||||
|
|
||||||
[*.{js,html}]
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.html]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -10,6 +10,7 @@ __pycache__/
|
|||||||
|
|
||||||
.env
|
.env
|
||||||
.venv
|
.venv
|
||||||
|
default.env
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
ENV/
|
ENV/
|
||||||
@ -17,8 +18,10 @@ env.bak/
|
|||||||
venv.bak/
|
venv.bak/
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
servers/
|
/servers/
|
||||||
backups/
|
/backups/
|
||||||
|
/docker/servers/
|
||||||
|
/docker/backups/
|
||||||
session.lock
|
session.lock
|
||||||
.header
|
.header
|
||||||
default.json
|
default.json
|
||||||
|
211
.gitlab-ci.yml
211
.gitlab-ci.yml
@ -1,35 +1,76 @@
|
|||||||
|
# Crafty Controller 4.0 - Lint & Build Pipes
|
||||||
|
# [Maintainer: Zedifus(https://gitlab.com/Zedifus)]
|
||||||
|
###################################################
|
||||||
|
# yamllint disable rule:line-length
|
||||||
|
---
|
||||||
stages:
|
stages:
|
||||||
- test
|
- lint
|
||||||
- prod-deployment
|
- prod-deployment
|
||||||
- dev-deployment
|
- dev-deployment
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DOCKER_HOST: tcp://docker:2376
|
DOCKER_HOST: tcp://docker:2376
|
||||||
DOCKER_TLS_CERTDIR: "/certs"
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
|
||||||
pylint:
|
yamllint:
|
||||||
stage: test
|
stage: lint
|
||||||
image: python:3.7-slim
|
image: registry.gitlab.com/pipeline-components/yamllint:latest
|
||||||
services:
|
|
||||||
- name: docker:dind
|
|
||||||
tags:
|
tags:
|
||||||
- 'docker_testers'
|
- "docker"
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
|
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||||
|
when: never
|
||||||
|
script:
|
||||||
|
- yamllint .
|
||||||
|
|
||||||
|
jsonlint:
|
||||||
|
stage: lint
|
||||||
|
image: registry.gitlab.com/pipeline-components/jsonlint:latest
|
||||||
|
tags:
|
||||||
|
- "docker"
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||||
when: never
|
when: never
|
||||||
before_script:
|
|
||||||
- mkdir -p public/badges public/lint
|
|
||||||
- echo undefined > public/badges/$CI_JOB_NAME.score
|
|
||||||
- pip install pylint-gitlab
|
|
||||||
script:
|
script:
|
||||||
- pylint --exit-zero --output-format=text $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") | tee /tmp/pylint.txt
|
|
||||||
- sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' /tmp/pylint.txt > public/badges/$CI_JOB_NAME.score
|
|
||||||
- pylint --exit-zero --output-format=pylint_gitlab.GitlabCodeClimateReporter $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") > codeclimate.json
|
|
||||||
after_script:
|
|
||||||
- anybadge --overwrite --label $CI_JOB_NAME --value=$(cat public/badges/$CI_JOB_NAME.score) --file=public/badges/$CI_JOB_NAME.svg 4=red 6=orange 8=yellow 10=green
|
|
||||||
- |
|
- |
|
||||||
echo "Your score is: $(cat public/badges/$CI_JOB_NAME.score)"
|
find . -not -path './.git/*' -name '*.json' -type f -print0 |
|
||||||
|
parallel --will-cite -k -0 -n1 jsonlint -q
|
||||||
|
|
||||||
|
black:
|
||||||
|
stage: lint
|
||||||
|
image: registry.gitlab.com/pipeline-components/black:latest
|
||||||
|
tags:
|
||||||
|
- "docker"
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||||
|
when: never
|
||||||
|
script:
|
||||||
|
- black --check --verbose -- .
|
||||||
|
|
||||||
|
pylint:
|
||||||
|
stage: lint
|
||||||
|
image: registry.gitlab.com/pipeline-components/pylint:latest
|
||||||
|
tags:
|
||||||
|
- "docker"
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||||
|
when: never
|
||||||
|
# before_script:
|
||||||
|
# - mkdir -p public/badges public/lint
|
||||||
|
# - echo undefined > public/badges/$CI_JOB_NAME.score
|
||||||
|
script:
|
||||||
|
# - pylint --exit-zero --output-format=text $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") | tee /tmp/pylint.txt
|
||||||
|
# - sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' /tmp/pylint.txt > public/badges/$CI_JOB_NAME.score
|
||||||
|
- pylint --exit-zero --output-format=pylint_gitlab.GitlabCodeClimateReporter $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") > codeclimate.json
|
||||||
|
# after_script:
|
||||||
|
# - anybadge --overwrite --label $CI_JOB_NAME --value=$(cat public/badges/$CI_JOB_NAME.score) --file=public/badges/$CI_JOB_NAME.svg 4=red 6=orange 8=yellow 10=green
|
||||||
|
# - |
|
||||||
|
# echo "Your score is: $(cat public/badges/$CI_JOB_NAME.score)"
|
||||||
|
# Removed lint badge generation until public release
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
@ -43,12 +84,18 @@ docker-build-dev:
|
|||||||
- name: docker:dind
|
- name: docker:dind
|
||||||
stage: dev-deployment
|
stage: dev-deployment
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- "docker_priv"
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == 'dev'
|
- if: $CI_COMMIT_BRANCH == 'dev'
|
||||||
environment:
|
environment:
|
||||||
name: development
|
name: development
|
||||||
before_script:
|
before_script:
|
||||||
|
- |
|
||||||
|
apk --no-cache add jq
|
||||||
|
MAJOR=$(cat app/config/version.json | jq '.major' )
|
||||||
|
MINOR=$(cat app/config/version.json | jq '.minor' )
|
||||||
|
SUB=$(cat app/config/version.json | jq '.sub' )
|
||||||
|
META=$(cat app/config/version.json | jq -r '.meta' )
|
||||||
- |
|
- |
|
||||||
apk --no-cache add curl
|
apk --no-cache add curl
|
||||||
latest_tag=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | sed -Ene '/^ *"tag_name": *"(v.+)",$/s//\1/p')
|
latest_tag=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | sed -Ene '/^ *"tag_name": *"(v.+)",$/s//\1/p')
|
||||||
@ -64,12 +111,18 @@ docker-build-dev:
|
|||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
tag=":$CI_COMMIT_REF_SLUG"
|
tag=":$CI_COMMIT_REF_SLUG"
|
||||||
|
VERSION="${MAJOR}.${MINOR}.${SUB}-${META}"
|
||||||
|
- |
|
||||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||||
|
echo "Crafty Version: $VERSION"
|
||||||
- docker context create tls-environment
|
- docker context create tls-environment
|
||||||
- docker buildx create --name zedBuilder --use tls-environment
|
- docker buildx create --name zedBuilder --use tls-environment
|
||||||
- docker buildx build
|
- docker buildx build
|
||||||
--cache-from type=registry,ref="$CI_REGISTRY_IMAGE${tag}"
|
--cache-from type=registry,ref="$CI_REGISTRY_IMAGE${tag}"
|
||||||
--build-arg BUILDKIT_INLINE_CACHE=1
|
--build-arg BUILDKIT_INLINE_CACHE=1
|
||||||
|
--build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
|
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
|
||||||
|
--build-arg "CRAFTY_VER=${VERSION}"
|
||||||
--tag "$CI_REGISTRY_IMAGE${tag}"
|
--tag "$CI_REGISTRY_IMAGE${tag}"
|
||||||
--platform linux/arm64/v8,linux/amd64
|
--platform linux/arm64/v8,linux/amd64
|
||||||
--push .
|
--push .
|
||||||
@ -86,12 +139,18 @@ docker-build-prod:
|
|||||||
- name: docker:dind
|
- name: docker:dind
|
||||||
stage: prod-deployment
|
stage: prod-deployment
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- "docker_priv"
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
before_script:
|
before_script:
|
||||||
|
- |
|
||||||
|
apk --no-cache add jq
|
||||||
|
MAJOR=$(cat app/config/version.json | jq '.major' )
|
||||||
|
MINOR=$(cat app/config/version.json | jq '.minor' )
|
||||||
|
SUB=$(cat app/config/version.json | jq '.sub' )
|
||||||
|
META=$(cat app/config/version.json | jq -r '.meta' )
|
||||||
- |
|
- |
|
||||||
apk --no-cache add curl
|
apk --no-cache add curl
|
||||||
latest_tag=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | sed -Ene '/^ *"tag_name": *"(v.+)",$/s//\1/p')
|
latest_tag=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | sed -Ene '/^ *"tag_name": *"(v.+)",$/s//\1/p')
|
||||||
@ -107,12 +166,18 @@ docker-build-prod:
|
|||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
tag=""
|
tag=""
|
||||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
|
VERSION="${MAJOR}.${MINOR}.${SUB}-${META}"
|
||||||
|
- |
|
||||||
|
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||||
|
echo "Crafty Version: $VERSION"
|
||||||
- docker context create tls-environment
|
- docker context create tls-environment
|
||||||
- docker buildx create --name zedBuilder --use tls-environment
|
- docker buildx create --name zedBuilder --use tls-environment
|
||||||
- docker buildx build
|
- docker buildx build
|
||||||
--cache-from type=registry,ref="$CI_REGISTRY_IMAGE${tag}"
|
--cache-from type=registry,ref="$CI_REGISTRY_IMAGE${tag}"
|
||||||
--build-arg BUILDKIT_INLINE_CACHE=1
|
--build-arg BUILDKIT_INLINE_CACHE=1
|
||||||
|
--build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
|
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
|
||||||
|
--build-arg "CRAFTY_VER=${VERSION}"
|
||||||
--tag "$CI_REGISTRY_IMAGE${tag}"
|
--tag "$CI_REGISTRY_IMAGE${tag}"
|
||||||
--platform linux/arm64/v8,linux/amd64
|
--platform linux/arm64/v8,linux/amd64
|
||||||
--push .
|
--push .
|
||||||
@ -126,81 +191,81 @@ docker-build-prod:
|
|||||||
win-dev-build:
|
win-dev-build:
|
||||||
stage: dev-deployment
|
stage: dev-deployment
|
||||||
tags:
|
tags:
|
||||||
- win64
|
- win64
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- .venv/
|
- .venv/
|
||||||
rules:
|
rules:
|
||||||
- if: "$CI_COMMIT_BRANCH == 'dev'"
|
- if: "$CI_COMMIT_BRANCH == 'dev'"
|
||||||
environment:
|
environment:
|
||||||
name: development
|
name: development
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
py -m venv .venv
|
py -m venv .venv
|
||||||
.venv\Scripts\activate.ps1
|
.venv\Scripts\activate.ps1
|
||||||
pip install pyinstaller
|
pip install pyinstaller
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
- pyinstaller -F main.py
|
- pyinstaller -F main.py
|
||||||
--distpath .
|
--distpath .
|
||||||
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
||||||
--name "crafty_commander"
|
--name "crafty_commander"
|
||||||
--paths .venv\Lib\site-packages
|
--paths .venv\Lib\site-packages
|
||||||
--hidden-import cryptography
|
--hidden-import cryptography
|
||||||
--hidden-import cffi
|
--hidden-import cffi
|
||||||
--hidden-import apscheduler
|
--hidden-import apscheduler
|
||||||
--collect-all tzlocal
|
--collect-all tzlocal
|
||||||
--collect-all tzdata
|
--collect-all tzdata
|
||||||
--collect-all pytz
|
--collect-all pytz
|
||||||
--collect-all six
|
--collect-all six
|
||||||
|
|
||||||
|
# Download latest:
|
||||||
|
# | https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/dev/download?job=win-dev-build
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
||||||
paths:
|
paths:
|
||||||
- app\
|
- app\
|
||||||
- .\crafty_commander.exe
|
- .\crafty_commander.exe
|
||||||
exclude:
|
exclude:
|
||||||
- app\classes\**\*
|
- app\classes\**\*
|
||||||
# Download latest:
|
|
||||||
# | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build
|
|
||||||
|
|
||||||
win-prod-build:
|
win-prod-build:
|
||||||
stage: prod-deployment
|
stage: prod-deployment
|
||||||
tags:
|
tags:
|
||||||
- win64
|
- win64
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- .venv/
|
- .venv/
|
||||||
rules:
|
rules:
|
||||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
py -m venv .venv
|
py -m venv .venv
|
||||||
.venv\Scripts\activate.ps1
|
.venv\Scripts\activate.ps1
|
||||||
pip install pyinstaller
|
pip install pyinstaller
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
- pyinstaller -F main.py
|
- pyinstaller -F main.py
|
||||||
--distpath .
|
--distpath .
|
||||||
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
||||||
--name "crafty_commander"
|
--name "crafty_commander"
|
||||||
--paths .venv\Lib\site-packages
|
--paths .venv\Lib\site-packages
|
||||||
--hidden-import cryptography
|
--hidden-import cryptography
|
||||||
--hidden-import cffi
|
--hidden-import cffi
|
||||||
--hidden-import apscheduler
|
--hidden-import apscheduler
|
||||||
--collect-all tzlocal
|
--collect-all tzlocal
|
||||||
--collect-all tzdata
|
--collect-all tzdata
|
||||||
--collect-all pytz
|
--collect-all pytz
|
||||||
--collect-all six
|
--collect-all six
|
||||||
|
|
||||||
|
# Download latest:
|
||||||
|
# | https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/master/download?job=win-prod-build
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
||||||
paths:
|
paths:
|
||||||
- app\
|
- app\
|
||||||
- .\crafty_commander.exe
|
- .\crafty_commander.exe
|
||||||
exclude:
|
exclude:
|
||||||
- app\classes\**\*
|
- app\classes\**\*
|
||||||
# Download latest:
|
|
||||||
# | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build
|
|
||||||
|
21
.pylintrc
21
.pylintrc
@ -78,7 +78,9 @@ confidence=
|
|||||||
# --enable=similarities". If you want to run only the classes checker, but have
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||||
# --disable=W".
|
# --disable=W".
|
||||||
disable=abstract-method,
|
disable=C0330,
|
||||||
|
C0326,
|
||||||
|
abstract-method,
|
||||||
attribute-defined-outside-init,
|
attribute-defined-outside-init,
|
||||||
bad-inline-option,
|
bad-inline-option,
|
||||||
bare-except,
|
bare-except,
|
||||||
@ -92,7 +94,6 @@ disable=abstract-method,
|
|||||||
fixme,
|
fixme,
|
||||||
import-error,
|
import-error,
|
||||||
inconsistent-return-statements,
|
inconsistent-return-statements,
|
||||||
invalid-name,
|
|
||||||
locally-disabled,
|
locally-disabled,
|
||||||
logging-format-interpolation,
|
logging-format-interpolation,
|
||||||
logging-fstring-interpolation,
|
logging-fstring-interpolation,
|
||||||
@ -234,10 +235,20 @@ function-naming-style=snake_case
|
|||||||
#function-rgx=
|
#function-rgx=
|
||||||
|
|
||||||
# Good variable names which should always be accepted, separated by a comma.
|
# Good variable names which should always be accepted, separated by a comma.
|
||||||
good-names=i,
|
good-names=e,
|
||||||
|
ex,
|
||||||
|
f,
|
||||||
|
i,
|
||||||
|
id,
|
||||||
|
ip,
|
||||||
j,
|
j,
|
||||||
k,
|
k,
|
||||||
ex,
|
p,
|
||||||
|
r,
|
||||||
|
rs,
|
||||||
|
s,
|
||||||
|
tz,
|
||||||
|
v,
|
||||||
Run,
|
Run,
|
||||||
_
|
_
|
||||||
|
|
||||||
@ -306,7 +317,7 @@ indent-after-paren=4
|
|||||||
indent-string=' '
|
indent-string=' '
|
||||||
|
|
||||||
# Maximum number of characters on a single line.
|
# Maximum number of characters on a single line.
|
||||||
max-line-length=150
|
max-line-length=88
|
||||||
|
|
||||||
# Maximum number of lines in a module.
|
# Maximum number of lines in a module.
|
||||||
max-module-lines=2000
|
max-module-lines=2000
|
||||||
|
29
Dockerfile
29
Dockerfile
@ -2,15 +2,13 @@ FROM ubuntu:20.04
|
|||||||
|
|
||||||
ENV DEBIAN_FRONTEND="noninteractive"
|
ENV DEBIAN_FRONTEND="noninteractive"
|
||||||
|
|
||||||
LABEL maintainer="Dockerfile created by Zedifus <https://gitlab.com/zedifus>"
|
|
||||||
|
|
||||||
# Security Patch for CVE-2021-44228
|
# Security Patch for CVE-2021-44228
|
||||||
ENV LOG4J_FORMAT_MSG_NO_LOOKUPS=true
|
ENV LOG4J_FORMAT_MSG_NO_LOOKUPS=true
|
||||||
|
|
||||||
# Create non-root user & required dirs
|
# Create non-root user & required dirs
|
||||||
RUN useradd -g root -M crafty \
|
RUN useradd -g root -M crafty \
|
||||||
&& mkdir /commander \
|
&& mkdir /crafty \
|
||||||
&& chown -R crafty:root /commander
|
&& chown -R crafty:root /crafty
|
||||||
|
|
||||||
# Install required system packages
|
# Install required system packages
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
@ -32,7 +30,7 @@ RUN apt-get update \
|
|||||||
|
|
||||||
# Switch to service user for installing crafty deps
|
# Switch to service user for installing crafty deps
|
||||||
USER crafty
|
USER crafty
|
||||||
WORKDIR /commander
|
WORKDIR /crafty
|
||||||
COPY --chown=crafty:root requirements.txt ./
|
COPY --chown=crafty:root requirements.txt ./
|
||||||
RUN python3 -m venv ./.venv \
|
RUN python3 -m venv ./.venv \
|
||||||
&& . .venv/bin/activate \
|
&& . .venv/bin/activate \
|
||||||
@ -53,6 +51,23 @@ EXPOSE 8443
|
|||||||
EXPOSE 19132
|
EXPOSE 19132
|
||||||
EXPOSE 25500-25600
|
EXPOSE 25500-25600
|
||||||
|
|
||||||
# Start Crafty Commander through wrapper
|
# Start Crafty through wrapper
|
||||||
ENTRYPOINT ["/commander/docker_launcher.sh"]
|
ENTRYPOINT ["/crafty/docker_launcher.sh"]
|
||||||
CMD ["-v", "-d", "-i"]
|
CMD ["-v", "-d", "-i"]
|
||||||
|
|
||||||
|
# Add meta labels
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG BUILD_REF
|
||||||
|
ARG CRAFTY_VER
|
||||||
|
LABEL \
|
||||||
|
maintainer="Zedifus <https://gitlab.com/zedifus>" \
|
||||||
|
org.opencontainers.image.created=${BUILD_DATE} \
|
||||||
|
org.opencontainers.image.revision=${BUILD_REF} \
|
||||||
|
org.opencontainers.image.version=${CRAFTY_VER} \
|
||||||
|
org.opencontainers.image.title="Crafty Controller" \
|
||||||
|
org.opencontainers.image.description="A Game Server Control Panel / Launcher" \
|
||||||
|
org.opencontainers.image.url="https://craftycontrol.com/" \
|
||||||
|
org.opencontainers.image.documentation="https://wiki.craftycontrol.com/" \
|
||||||
|
org.opencontainers.image.source="https://gitlab.com/crafty-controller/crafty-4" \
|
||||||
|
org.opencontainers.image.vendor="Arcadia Technology, LLC." \
|
||||||
|
org.opencontainers.image.licenses="GPL-3.0"
|
||||||
|
88
README.md
88
README.md
@ -1,4 +1,12 @@
|
|||||||
# Crafty Controller 4.0.0-alpha.3.5
|
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||||
|
|
||||||
|
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||||
|
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
||||||
|
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.0--beta-orange)](https://gitlab.com/crafty-controller/crafty-4)
|
||||||
|
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
||||||
|
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
||||||
|
|
||||||
|
# Crafty Controller 4.0.0-beta
|
||||||
> Python based Control Panel for your Minecraft Server
|
> Python based Control Panel for your Minecraft Server
|
||||||
|
|
||||||
## What is Crafty Controller?
|
## What is Crafty Controller?
|
||||||
@ -15,7 +23,7 @@ Project Homepage - https://craftycontrol.com
|
|||||||
|
|
||||||
Discord Server - https://discord.gg/9VJPhCE
|
Discord Server - https://discord.gg/9VJPhCE
|
||||||
|
|
||||||
Git Repository - https://gitlab.com/crafty-controller/crafty-web
|
Git Repository - https://gitlab.com/crafty-controller/crafty-4
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@ -31,7 +39,7 @@ With `Crafty Controller 4.0` we have focused on building our DevOps Principles,
|
|||||||
> __**⚠ 🔻WARNING: [WSL/WSL2 | WINDOWS 11 | DOCKER DESKTOP]🔻**__ <br>
|
> __**⚠ 🔻WARNING: [WSL/WSL2 | WINDOWS 11 | DOCKER DESKTOP]🔻**__ <br>
|
||||||
BE ADVISED! Upstream is currently broken for Minecraft running on **Docker under WSL/WSL2, Windows 11 / DOCKER DESKTOP!** <br>
|
BE ADVISED! Upstream is currently broken for Minecraft running on **Docker under WSL/WSL2, Windows 11 / DOCKER DESKTOP!** <br>
|
||||||
On '**Stop**' or '**Restart**' of the MC Server, there is a 90% chance the World's Chunks will be shredded irreparably! <br>
|
On '**Stop**' or '**Restart**' of the MC Server, there is a 90% chance the World's Chunks will be shredded irreparably! <br>
|
||||||
Please only run Docker on Linux, If you are using Windows we have a portable installs found here: [Latest-Stable](https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build), [Latest-Development](https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build)
|
Please only run Docker on Linux, If you are using Windows we have a portable installs found here: [Latest-Stable](https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/master/download?job=win-prod-build), [Latest-Development](https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/dev/download?job=win-dev-build)
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -47,35 +55,16 @@ As the Dockerfile uses the permission structure of `crafty:root` **internally**
|
|||||||
### - Using the registry image 🌎
|
### - Using the registry image 🌎
|
||||||
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
|
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
|
||||||
|
|
||||||
The image is located at: `registry.gitlab.com/crafty-controller/crafty-commander:latest`
|
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest`
|
||||||
| Branch | Status |
|
| Branch | Status |
|
||||||
| ----------------- | ------------------------------------------------------------------ |
|
| ----------------- | ------------------------------------------------------------------ |
|
||||||
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-commander/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/master) |
|
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) |
|
||||||
| :dev | [![pipeline status](https://gitlab.com/crafty-controller/crafty-commander/badges/dev/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/dev) |
|
| :dev | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/dev/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/dev)
|
||||||
|
|
||||||
While the repository is still **private / pre-release**,
|
<br>
|
||||||
Before you can pull the image you must authenticate docker with the Container Registry.
|
|
||||||
|
|
||||||
To authenticate you will need a [personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
|
**Here are some example methods for getting started🚀:**
|
||||||
with the minimum scope:
|
|
||||||
|
|
||||||
- For read (*pull*) access, `read_registry`.
|
|
||||||
- For write (*push*) access, `write_registry`.
|
|
||||||
|
|
||||||
When you have this just run:
|
|
||||||
```bash
|
|
||||||
$ docker login registry.gitlab.com -u <username> -p <token>
|
|
||||||
```
|
|
||||||
or
|
|
||||||
```bash
|
|
||||||
$ echo <token> | docker login registry.gitlab.com -u <username> --password-stdin
|
|
||||||
```
|
|
||||||
or
|
|
||||||
```bash
|
|
||||||
$ cat ~/my_password.txt | docker login registry.gitlab.com -u <username> --password-stdin
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use one of the following methods:
|
|
||||||
### **docker-compose.yml:**
|
### **docker-compose.yml:**
|
||||||
```sh
|
```sh
|
||||||
# Make your compose file
|
# Make your compose file
|
||||||
@ -86,8 +75,9 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
crafty:
|
crafty:
|
||||||
container_name: crafty_commander
|
container_name: crafty_container
|
||||||
image: registry.gitlab.com/crafty-controller/crafty-commander:latest
|
image: registry.gitlab.com/crafty-controller/crafty-4:latest
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- TZ=Etc/UTC
|
- TZ=Etc/UTC
|
||||||
ports:
|
ports:
|
||||||
@ -97,11 +87,11 @@ services:
|
|||||||
- "19132:19132/udp" # BEDROCK
|
- "19132:19132/udp" # BEDROCK
|
||||||
- "25500-25600:25500-25600" # MC SERV PORT RANGE
|
- "25500-25600:25500-25600" # MC SERV PORT RANGE
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/backups:/commander/backups
|
- ./docker/backups:/crafty/backups
|
||||||
- ./docker/logs:/commander/logs
|
- ./docker/logs:/crafty/logs
|
||||||
- ./docker/servers:/commander/servers
|
- ./docker/servers:/crafty/servers
|
||||||
- ./docker/config:/commander/app/config
|
- ./docker/config:/crafty/app/config
|
||||||
- ./docker/import:/commander/import
|
- ./docker/import:/crafty/import
|
||||||
```
|
```
|
||||||
```sh
|
```sh
|
||||||
$ docker-compose up -d && docker-compose logs -f
|
$ docker-compose up -d && docker-compose logs -f
|
||||||
@ -111,19 +101,21 @@ $ docker-compose up -d && docker-compose logs -f
|
|||||||
### **docker run:**
|
### **docker run:**
|
||||||
```sh
|
```sh
|
||||||
$ docker run \
|
$ docker run \
|
||||||
--name crafty_commander \
|
--name crafty_container \
|
||||||
|
--detach \
|
||||||
|
--restart always \
|
||||||
-p 8000:8000 \
|
-p 8000:8000 \
|
||||||
-p 8443:8443 \
|
-p 8443:8443 \
|
||||||
-p 8123:8123 \
|
-p 8123:8123 \
|
||||||
-p 19132:19132/udp \
|
-p 19132:19132/udp \
|
||||||
-p 25500-25600:25500-25600 \
|
-p 25500-25600:25500-25600 \
|
||||||
-e TZ=Etc/UTC \
|
-e TZ=Etc/UTC \
|
||||||
-v "/$(pwd)/docker/backups:/commander/backups" \
|
-v "/$(pwd)/docker/backups:/crafty/backups" \
|
||||||
-v "/$(pwd)/docker/logs:/commander/logs" \
|
-v "/$(pwd)/docker/logs:/crafty/logs" \
|
||||||
-v "/$(pwd)/docker/servers:/commander/servers" \
|
-v "/$(pwd)/docker/servers:/crafty/servers" \
|
||||||
-v "/$(pwd)/docker/config:/commander/app/config" \
|
-v "/$(pwd)/docker/config:/crafty/app/config" \
|
||||||
-v "/$(pwd)/docker/import:/commander/import" \
|
-v "/$(pwd)/docker/import:/crafty/import" \
|
||||||
registry.gitlab.com/crafty-controller/crafty-commander:latest
|
registry.gitlab.com/crafty-controller/crafty-4:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Building from the cloned repository:**
|
### **Building from the cloned repository:**
|
||||||
@ -136,18 +128,20 @@ If you'd rather not use `docker-compose` you can use the following `docker run`
|
|||||||
$ docker build . -t crafty
|
$ docker build . -t crafty
|
||||||
|
|
||||||
$ docker run \
|
$ docker run \
|
||||||
--name crafty_commander \
|
--name crafty_container \
|
||||||
|
--detach \
|
||||||
|
--restart always \
|
||||||
-p 8000:8000 \
|
-p 8000:8000 \
|
||||||
-p 8443:8443 \
|
-p 8443:8443 \
|
||||||
-p 8123:8123 \
|
-p 8123:8123 \
|
||||||
-p 19132:19132/udp \
|
-p 19132:19132/udp \
|
||||||
-p 25500-25600:25500-25600 \
|
-p 25500-25600:25500-25600 \
|
||||||
-e TZ=Etc/UTC \
|
-e TZ=Etc/UTC \
|
||||||
-v "/$(pwd)/docker/backups:/commander/backups" \
|
-v "/$(pwd)/docker/backups:/crafty/backups" \
|
||||||
-v "/$(pwd)/docker/logs:/commander/logs" \
|
-v "/$(pwd)/docker/logs:/crafty/logs" \
|
||||||
-v "/$(pwd)/docker/servers:/commander/servers" \
|
-v "/$(pwd)/docker/servers:/crafty/servers" \
|
||||||
-v "/$(pwd)/docker/config:/commander/app/config" \
|
-v "/$(pwd)/docker/config:/crafty/app/config" \
|
||||||
-v "/$(pwd)/docker/import:/commander/import" \
|
-v "/$(pwd)/docker/import:/crafty/import" \
|
||||||
crafty
|
crafty
|
||||||
```
|
```
|
||||||
A fresh build will take several minutes depending on your system, but will be rapid thereafter.
|
A fresh build will take several minutes depending on your system, but will be rapid thereafter.
|
||||||
|
@ -1,60 +1,85 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
from app.classes.models.crafty_permissions import (
|
||||||
|
PermissionsCrafty,
|
||||||
|
EnumPermissionsCrafty,
|
||||||
|
)
|
||||||
from app.classes.models.users import ApiKeys
|
from app.classes.models.users import ApiKeys
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Crafty_Perms_Controller:
|
|
||||||
|
|
||||||
|
class CraftyPermsController:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_defined_crafty_permissions():
|
def list_defined_crafty_permissions():
|
||||||
permissions_list = crafty_permissions.get_permissions_list()
|
permissions_list = PermissionsCrafty.get_permissions_list()
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_mask_crafty_permissions(user_id):
|
def get_mask_crafty_permissions(user_id):
|
||||||
permissions_mask = crafty_permissions.get_crafty_permissions_mask(user_id)
|
permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(user_id)
|
||||||
return permissions_mask
|
return permissions_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value):
|
def set_permission(
|
||||||
return crafty_permissions.set_permission(permission_mask, permission_tested, value)
|
permission_mask, permission_tested: EnumPermissionsCrafty, value
|
||||||
|
):
|
||||||
|
return PermissionsCrafty.set_permission(
|
||||||
|
permission_mask, permission_tested, value
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_create_server(user_id):
|
def can_create_server(user_id):
|
||||||
return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Server_Creation)
|
return PermissionsCrafty.can_add_in_crafty(
|
||||||
|
user_id, EnumPermissionsCrafty.SERVER_CREATION
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_add_user(): # Add back argument 'user_id' when you work on this
|
def can_add_user(user_id):
|
||||||
#TODO: Complete if we need a User Addition limit
|
return PermissionsCrafty.can_add_in_crafty(
|
||||||
#return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.User_Config)
|
user_id, EnumPermissionsCrafty.USER_CONFIG
|
||||||
return True
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_add_role(): # Add back argument 'user_id' when you work on this
|
def can_add_role(user_id):
|
||||||
#TODO: Complete if we need a Role Addition limit
|
return PermissionsCrafty.can_add_in_crafty(
|
||||||
#return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config)
|
user_id, EnumPermissionsCrafty.ROLES_CONFIG
|
||||||
return True
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_all_crafty_permissions_quantity_limits():
|
def list_all_crafty_permissions_quantity_limits():
|
||||||
return crafty_permissions.get_all_permission_quantity_list()
|
return PermissionsCrafty.get_all_permission_quantity_list()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_crafty_permissions_quantity_limits(user_id):
|
def list_crafty_permissions_quantity_limits(user_id):
|
||||||
return crafty_permissions.get_permission_quantity_list(user_id)
|
return PermissionsCrafty.get_permission_quantity_list(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_crafty_permissions_list(user_id):
|
def get_crafty_permissions_list(user_id):
|
||||||
permissions_mask = crafty_permissions.get_crafty_permissions_mask(user_id)
|
permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(user_id)
|
||||||
permissions_list = crafty_permissions.get_permissions(permissions_mask)
|
permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_server_creation(user_id):
|
def add_server_creation(user_id):
|
||||||
return crafty_permissions.add_server_creation(user_id)
|
"""Increase the "Server Creation" counter for this user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (int): The modifiable user's ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The new count of servers created by this user
|
||||||
|
"""
|
||||||
|
return PermissionsCrafty.add_server_creation(user_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_user_creation(user_id):
|
||||||
|
return PermissionsCrafty.add_user_creation(user_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_role_creation(user_id):
|
||||||
|
return PermissionsCrafty.add_role_creation(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_permissions_list(key: ApiKeys):
|
def get_api_key_permissions_list(key: ApiKeys):
|
||||||
return crafty_permissions.get_api_key_permissions_list(key)
|
return PermissionsCrafty.get_api_key_permissions_list(key)
|
||||||
|
@ -1,120 +1,144 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.models.management import management_helper
|
from app.classes.models.management import HelpersManagement
|
||||||
from app.classes.models.servers import servers_helper
|
from app.classes.models.servers import HelperServers
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Management_Controller:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class ManagementController:
|
||||||
|
def __init__(self, management_helper):
|
||||||
|
self.management_helper = management_helper
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# Host_Stats Methods
|
# Host_Stats Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_latest_hosts_stats():
|
def get_latest_hosts_stats():
|
||||||
return management_helper.get_latest_hosts_stats()
|
return HelpersManagement.get_latest_hosts_stats()
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Commands Methods
|
# Commands Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_unactioned_commands():
|
def get_unactioned_commands():
|
||||||
return management_helper.get_unactioned_commands()
|
return HelpersManagement.get_unactioned_commands()
|
||||||
|
|
||||||
@staticmethod
|
def send_command(self, user_id, server_id, remote_ip, command):
|
||||||
def send_command(user_id, server_id, remote_ip, command):
|
server_name = HelperServers.get_server_friendly_name(server_id)
|
||||||
server_name = servers_helper.get_server_friendly_name(server_id)
|
|
||||||
|
|
||||||
# Example: Admin issued command start_server for server Survival
|
# Example: Admin issued command start_server for server Survival
|
||||||
management_helper.add_to_audit_log(user_id, f"issued command {command} for server {server_name}", server_id, remote_ip)
|
self.management_helper.add_to_audit_log(
|
||||||
management_helper.add_command(server_id, user_id, remote_ip, command)
|
user_id,
|
||||||
|
f"issued command {command} for server {server_name}",
|
||||||
|
server_id,
|
||||||
|
remote_ip,
|
||||||
|
)
|
||||||
|
HelpersManagement.add_command(server_id, user_id, remote_ip, command)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mark_command_complete(command_id=None):
|
def mark_command_complete(command_id=None):
|
||||||
return management_helper.mark_command_complete(command_id)
|
return HelpersManagement.mark_command_complete(command_id)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# Audit_Log Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_actity_log():
|
def get_actity_log():
|
||||||
return management_helper.get_actity_log()
|
return HelpersManagement.get_actity_log()
|
||||||
|
|
||||||
@staticmethod
|
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
|
||||||
def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None):
|
return self.management_helper.add_to_audit_log(
|
||||||
return management_helper.add_to_audit_log(user_id, log_msg, server_id, source_ip)
|
user_id, log_msg, server_id, source_ip
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
||||||
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
|
return self.management_helper.add_to_audit_log_raw(
|
||||||
return management_helper.add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip)
|
user_name, user_id, server_id, log_msg, source_ip
|
||||||
|
)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Schedules Methods
|
# Schedules Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True):
|
def create_scheduled_task(
|
||||||
return management_helper.create_scheduled_task(
|
server_id,
|
||||||
server_id,
|
action,
|
||||||
action,
|
interval,
|
||||||
interval,
|
interval_type,
|
||||||
interval_type,
|
start_time,
|
||||||
start_time,
|
command,
|
||||||
command,
|
comment=None,
|
||||||
comment,
|
enabled=True,
|
||||||
enabled
|
):
|
||||||
)
|
return HelpersManagement.create_scheduled_task(
|
||||||
|
server_id,
|
||||||
|
action,
|
||||||
|
interval,
|
||||||
|
interval_type,
|
||||||
|
start_time,
|
||||||
|
command,
|
||||||
|
comment,
|
||||||
|
enabled,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_scheduled_task(schedule_id):
|
def delete_scheduled_task(schedule_id):
|
||||||
return management_helper.delete_scheduled_task(schedule_id)
|
return HelpersManagement.delete_scheduled_task(schedule_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_scheduled_task(schedule_id, updates):
|
def update_scheduled_task(schedule_id, updates):
|
||||||
return management_helper.update_scheduled_task(schedule_id, updates)
|
return HelpersManagement.update_scheduled_task(schedule_id, updates)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_scheduled_task(schedule_id):
|
def get_scheduled_task(schedule_id):
|
||||||
return management_helper.get_scheduled_task(schedule_id)
|
return HelpersManagement.get_scheduled_task(schedule_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_scheduled_task_model(schedule_id):
|
def get_scheduled_task_model(schedule_id):
|
||||||
return management_helper.get_scheduled_task_model(schedule_id)
|
return HelpersManagement.get_scheduled_task_model(schedule_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_child_schedules(sch_id):
|
def get_child_schedules(sch_id):
|
||||||
return management_helper.get_child_schedules(sch_id)
|
return HelpersManagement.get_child_schedules(sch_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_schedules_by_server(server_id):
|
def get_schedules_by_server(server_id):
|
||||||
return management_helper.get_schedules_by_server(server_id)
|
return HelpersManagement.get_schedules_by_server(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_schedules_all():
|
def get_schedules_all():
|
||||||
return management_helper.get_schedules_all()
|
return HelpersManagement.get_schedules_all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_schedules_enabled():
|
def get_schedules_enabled():
|
||||||
return management_helper.get_schedules_enabled()
|
return HelpersManagement.get_schedules_enabled()
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Backups Methods
|
# Backups Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_backup_config(server_id):
|
def get_backup_config(server_id):
|
||||||
return management_helper.get_backup_config(server_id)
|
return HelpersManagement.get_backup_config(server_id)
|
||||||
|
|
||||||
@staticmethod
|
def set_backup_config(
|
||||||
def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, excluded_dirs: list = None, compress: bool = False,):
|
self,
|
||||||
return management_helper.set_backup_config(server_id, backup_path, max_backups, excluded_dirs, compress)
|
server_id: int,
|
||||||
|
backup_path: str = None,
|
||||||
|
max_backups: int = None,
|
||||||
|
excluded_dirs: list = None,
|
||||||
|
compress: bool = False,
|
||||||
|
):
|
||||||
|
return self.management_helper.set_backup_config(
|
||||||
|
server_id, backup_path, max_backups, excluded_dirs, compress
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_excluded_backup_dirs(server_id: int):
|
def get_excluded_backup_dirs(server_id: int):
|
||||||
return management_helper.get_excluded_backup_dirs(server_id)
|
return HelpersManagement.get_excluded_backup_dirs(server_id)
|
||||||
|
|
||||||
@staticmethod
|
def add_excluded_backup_dir(self, server_id: int, dir_to_add: str):
|
||||||
def add_excluded_backup_dir(server_id: int, dir_to_add: str):
|
self.management_helper.add_excluded_backup_dir(server_id, dir_to_add)
|
||||||
management_helper.add_excluded_backup_dir(server_id, dir_to_add)
|
|
||||||
|
|
||||||
@staticmethod
|
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
|
||||||
def del_excluded_backup_dir(server_id: int, dir_to_del: str):
|
self.management_helper.del_excluded_backup_dir(server_id, dir_to_del)
|
||||||
management_helper.del_excluded_backup_dir(server_id, dir_to_del)
|
|
||||||
|
@ -1,32 +1,39 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from app.classes.models.roles import roles_helper
|
from app.classes.models.roles import HelperRoles
|
||||||
from app.classes.models.server_permissions import server_permissions
|
from app.classes.models.server_permissions import PermissionsServers, RoleServers
|
||||||
from app.classes.models.users import users_helper
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Roles_Controller:
|
|
||||||
|
class RolesController:
|
||||||
|
def __init__(self, users_helper, roles_helper):
|
||||||
|
self.users_helper = users_helper
|
||||||
|
self.roles_helper = roles_helper
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_roles():
|
def get_all_roles():
|
||||||
return roles_helper.get_all_roles()
|
return HelperRoles.get_all_roles()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_role_ids():
|
||||||
|
return HelperRoles.get_all_role_ids()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_roleid_by_name(role_name):
|
def get_roleid_by_name(role_name):
|
||||||
return roles_helper.get_roleid_by_name(role_name)
|
return HelperRoles.get_roleid_by_name(role_name)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_role(role_id):
|
def get_role(role_id):
|
||||||
return roles_helper.get_role(role_id)
|
return HelperRoles.get_role(role_id)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_role(role_id: str, role_data = None, permissions_mask: str = "00000000"):
|
def update_role(role_id: str, role_data=None, permissions_mask: str = "00000000"):
|
||||||
if role_data is None:
|
if role_data is None:
|
||||||
role_data = {}
|
role_data = {}
|
||||||
base_data = Roles_Controller.get_role_with_servers(role_id)
|
base_data = RolesController.get_role_with_servers(role_id)
|
||||||
up_data = {}
|
up_data = {}
|
||||||
added_servers = set()
|
added_servers = set()
|
||||||
removed_servers = set()
|
removed_servers = set()
|
||||||
@ -34,49 +41,140 @@ class Roles_Controller:
|
|||||||
if key == "role_id":
|
if key == "role_id":
|
||||||
continue
|
continue
|
||||||
elif key == "servers":
|
elif key == "servers":
|
||||||
added_servers = role_data['servers'].difference(base_data['servers'])
|
added_servers = set(role_data["servers"]).difference(
|
||||||
removed_servers = base_data['servers'].difference(role_data['servers'])
|
set(base_data["servers"])
|
||||||
|
)
|
||||||
|
removed_servers = set(base_data["servers"]).difference(
|
||||||
|
set(role_data["servers"])
|
||||||
|
)
|
||||||
elif base_data[key] != role_data[key]:
|
elif base_data[key] != role_data[key]:
|
||||||
up_data[key] = role_data[key]
|
up_data[key] = role_data[key]
|
||||||
up_data['last_update'] = helper.get_time_as_string()
|
up_data["last_update"] = Helpers.get_time_as_string()
|
||||||
logger.debug(f"role: {role_data} +server:{added_servers} -server{removed_servers}")
|
logger.debug(
|
||||||
|
f"role: {role_data} +server:{added_servers} -server{removed_servers}"
|
||||||
|
)
|
||||||
for server in added_servers:
|
for server in added_servers:
|
||||||
server_permissions.get_or_create(role_id, server, permissions_mask)
|
PermissionsServers.get_or_create(role_id, server, permissions_mask)
|
||||||
for server in base_data['servers']:
|
for server in base_data["servers"]:
|
||||||
server_permissions.update_role_permission(role_id, server, permissions_mask)
|
PermissionsServers.update_role_permission(role_id, server, permissions_mask)
|
||||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
# TODO: This is horribly inefficient and we should be using bulk queries
|
||||||
server_permissions.delete_roles_permissions(role_id, removed_servers)
|
# but im going for functionality at this point
|
||||||
|
PermissionsServers.delete_roles_permissions(role_id, removed_servers)
|
||||||
if up_data:
|
if up_data:
|
||||||
roles_helper.update_role(role_id, up_data)
|
HelperRoles.update_role(role_id, up_data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_role(role_name):
|
def add_role(role_name):
|
||||||
return roles_helper.add_role(role_name)
|
return HelperRoles.add_role(role_name)
|
||||||
|
|
||||||
|
class RoleServerJsonType(t.TypedDict):
|
||||||
|
server_id: t.Union[str, int]
|
||||||
|
permissions: str
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_role(role_id):
|
def get_server_ids_and_perms_from_role(
|
||||||
role_data = Roles_Controller.get_role_with_servers(role_id)
|
role_id: t.Union[str, int]
|
||||||
server_permissions.delete_roles_permissions(role_id, role_data['servers'])
|
) -> t.List[RoleServerJsonType]:
|
||||||
users_helper.remove_roles_from_role_id(role_id)
|
# FIXME: somehow retrieve only the server ids, not the whole servers
|
||||||
return roles_helper.remove_role(role_id)
|
return [
|
||||||
|
{
|
||||||
|
"server_id": role_servers.server_id.server_id,
|
||||||
|
"permissions": role_servers.permissions,
|
||||||
|
}
|
||||||
|
for role_servers in (
|
||||||
|
RoleServers.select(
|
||||||
|
RoleServers.server_id, RoleServers.permissions
|
||||||
|
).where(RoleServers.role_id == role_id)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_role_advanced(
|
||||||
|
name: str,
|
||||||
|
servers: t.Iterable[RoleServerJsonType],
|
||||||
|
) -> int:
|
||||||
|
"""Add a role with a name and a list of servers
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The new role's name
|
||||||
|
servers (t.List[RoleServerJsonType]): The new role's servers
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The new role's ID
|
||||||
|
"""
|
||||||
|
role_id: t.Final[int] = HelperRoles.add_role(name)
|
||||||
|
for server in servers:
|
||||||
|
PermissionsServers.get_or_create(
|
||||||
|
role_id, server["server_id"], server["permissions"]
|
||||||
|
)
|
||||||
|
return role_id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_role_advanced(
|
||||||
|
role_id: t.Union[str, int],
|
||||||
|
role_name: t.Optional[str],
|
||||||
|
servers: t.Optional[t.Iterable[RoleServerJsonType]],
|
||||||
|
) -> None:
|
||||||
|
"""Update a role with a name and a list of servers
|
||||||
|
|
||||||
|
Args:
|
||||||
|
role_id (t.Union[str, int]): The ID of the role to be modified
|
||||||
|
role_name (t.Optional[str]): An optional new name for the role
|
||||||
|
servers (t.Optional[t.Iterable[RoleServerJsonType]]): An optional list of servers for the role
|
||||||
|
""" # pylint: disable=line-too-long
|
||||||
|
logger.debug(f"updating role {role_id} with advanced options")
|
||||||
|
|
||||||
|
if servers is not None:
|
||||||
|
base_data = RolesController.get_role_with_servers(role_id)
|
||||||
|
|
||||||
|
server_ids = {server["server_id"] for server in servers}
|
||||||
|
server_permissions_map = {
|
||||||
|
server["server_id"]: server["permissions"] for server in servers
|
||||||
|
}
|
||||||
|
|
||||||
|
added_servers = server_ids.difference(set(base_data["servers"]))
|
||||||
|
removed_servers = set(base_data["servers"]).difference(server_ids)
|
||||||
|
same_servers = server_ids.intersection(set(base_data["servers"]))
|
||||||
|
logger.debug(
|
||||||
|
f"role: {role_id} +server:{added_servers} -server{removed_servers}"
|
||||||
|
)
|
||||||
|
for server_id in added_servers:
|
||||||
|
PermissionsServers.get_or_create(
|
||||||
|
role_id, server_id, server_permissions_map[server_id]
|
||||||
|
)
|
||||||
|
if len(removed_servers) != 0:
|
||||||
|
PermissionsServers.delete_roles_permissions(role_id, removed_servers)
|
||||||
|
for server_id in same_servers:
|
||||||
|
PermissionsServers.update_role_permission(
|
||||||
|
role_id, server_id, server_permissions_map[server_id]
|
||||||
|
)
|
||||||
|
if role_name is not None:
|
||||||
|
up_data = {
|
||||||
|
"role_name": role_name,
|
||||||
|
"last_update": Helpers.get_time_as_string(),
|
||||||
|
}
|
||||||
|
# TODO: do the last_update on the db side
|
||||||
|
HelperRoles.update_role(role_id, up_data)
|
||||||
|
|
||||||
|
def remove_role(self, role_id):
|
||||||
|
role_data = RolesController.get_role_with_servers(role_id)
|
||||||
|
PermissionsServers.delete_roles_permissions(role_id, role_data["servers"])
|
||||||
|
self.users_helper.remove_roles_from_role_id(role_id)
|
||||||
|
return self.roles_helper.remove_role(role_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def role_id_exists(role_id):
|
def role_id_exists(role_id):
|
||||||
return roles_helper.role_id_exists(role_id)
|
return HelperRoles.role_id_exists(role_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_role_with_servers(role_id):
|
def get_role_with_servers(role_id):
|
||||||
role = roles_helper.get_role(role_id)
|
role = HelperRoles.get_role(role_id)
|
||||||
|
|
||||||
if role:
|
if role:
|
||||||
servers_query = server_permissions.get_servers_from_role(role_id)
|
server_ids = PermissionsServers.get_server_ids_from_role(role_id)
|
||||||
# TODO: this query needs to be narrower
|
role["servers"] = list(server_ids)
|
||||||
servers = set()
|
# logger.debug("role: ({}) {}".format(role_id, role))
|
||||||
for s in servers_query:
|
|
||||||
servers.add(s.server_id.server_id)
|
|
||||||
role['servers'] = servers
|
|
||||||
#logger.debug("role: ({}) {}".format(role_id, role))
|
|
||||||
return role
|
return role
|
||||||
else:
|
else:
|
||||||
#logger.debug("role: ({}) {}".format(role_id, {}))
|
# logger.debug("role: ({}) {}".format(role_id, {}))
|
||||||
return {}
|
return {}
|
||||||
|
@ -1,99 +1,117 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
from app.classes.models.server_permissions import (
|
||||||
from app.classes.models.users import users_helper, ApiKeys
|
PermissionsServers,
|
||||||
from app.classes.models.roles import roles_helper
|
EnumPermissionsServer,
|
||||||
from app.classes.models.servers import servers_helper
|
)
|
||||||
from app.classes.shared.main_models import db_helper
|
from app.classes.models.users import HelperUsers, ApiKeys
|
||||||
|
from app.classes.models.roles import HelperRoles
|
||||||
|
from app.classes.models.servers import HelperServers
|
||||||
|
from app.classes.models.server_stats import HelperServerStats
|
||||||
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Server_Perms_Controller:
|
|
||||||
|
|
||||||
|
class ServerPermsController:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_user_list(server_id):
|
def get_server_user_list(server_id):
|
||||||
return server_permissions.get_server_user_list(server_id)
|
return PermissionsServers.get_server_user_list(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_defined_permissions():
|
def list_defined_permissions():
|
||||||
permissions_list = server_permissions.get_permissions_list()
|
permissions_list = PermissionsServers.get_permissions_list()
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_mask_permissions(role_id, server_id):
|
def get_mask_permissions(role_id, server_id):
|
||||||
permissions_mask = server_permissions.get_permissions_mask(role_id, server_id)
|
permissions_mask = PermissionsServers.get_permissions_mask(role_id, server_id)
|
||||||
return permissions_mask
|
return permissions_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_role_permissions(role_id):
|
def get_role_permissions_dict(role_id):
|
||||||
permissions_list = server_permissions.get_role_permissions_list(role_id)
|
return PermissionsServers.get_role_permissions_dict(role_id)
|
||||||
return permissions_list
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
||||||
return server_permissions.add_role_server(server_id, role_id, rs_permissions)
|
return PermissionsServers.add_role_server(server_id, role_id, rs_permissions)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_roles(server_id):
|
def get_server_roles(server_id):
|
||||||
return server_permissions.get_server_roles(server_id)
|
return PermissionsServers.get_server_roles(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def backup_role_swap(old_server_id, new_server_id):
|
def backup_role_swap(old_server_id, new_server_id):
|
||||||
role_list = server_permissions.get_server_roles(old_server_id)
|
role_list = PermissionsServers.get_server_roles(old_server_id)
|
||||||
for role in role_list:
|
for role in role_list:
|
||||||
server_permissions.add_role_server(
|
PermissionsServers.add_role_server(
|
||||||
new_server_id, role.role_id,
|
new_server_id,
|
||||||
server_permissions.get_permissions_mask(int(role.role_id), int(old_server_id)))
|
role.role_id,
|
||||||
#server_permissions.add_role_server(new_server_id, role.role_id, '00001000')
|
PermissionsServers.get_permissions_mask(
|
||||||
|
int(role.role_id), int(old_server_id)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# Permissions_Servers.add_role_server(
|
||||||
|
# new_server_id, role.role_id, "00001000"
|
||||||
|
# )
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Permissions Methods
|
# Servers Permissions Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions_mask(role_id, server_id):
|
def get_permissions_mask(role_id, server_id):
|
||||||
return server_permissions.get_permissions_mask(role_id, server_id)
|
return PermissionsServers.get_permissions_mask(role_id, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value):
|
def set_permission(
|
||||||
return server_permissions.set_permission(permission_mask, permission_tested, value)
|
permission_mask, permission_tested: EnumPermissionsServer, value
|
||||||
|
):
|
||||||
@staticmethod
|
return PermissionsServers.set_permission(
|
||||||
def get_role_permissions_list(role_id):
|
permission_mask, permission_tested, value
|
||||||
return server_permissions.get_role_permissions_list(role_id)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_id_permissions_list(user_id: str, server_id: str):
|
def get_user_id_permissions_list(user_id: str, server_id: str):
|
||||||
return server_permissions.get_user_id_permissions_list(user_id, server_id)
|
return PermissionsServers.get_user_id_permissions_list(user_id, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
||||||
key = users_helper.get_user_api_key(key_id)
|
key = HelperUsers.get_user_api_key(key_id)
|
||||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
return PermissionsServers.get_api_key_permissions_list(key, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
return PermissionsServers.get_api_key_permissions_list(key, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_authorized_servers_stats_from_roles(user_id):
|
def get_authorized_servers_stats_from_roles(user_id):
|
||||||
user_roles = users_helper.get_user_roles_id(user_id)
|
user_roles = HelperUsers.get_user_roles_id(user_id)
|
||||||
roles_list = []
|
roles_list = []
|
||||||
role_server = []
|
role_server = []
|
||||||
authorized_servers = []
|
authorized_servers = []
|
||||||
server_data = []
|
server_data = []
|
||||||
|
|
||||||
for u in user_roles:
|
for user in user_roles:
|
||||||
roles_list.append(roles_helper.get_role(u.role_id))
|
roles_list.append(HelperRoles.get_role(user.role_id))
|
||||||
|
|
||||||
for r in roles_list:
|
for role in roles_list:
|
||||||
role_test = server_permissions.get_role_servers_from_role_id(r.get('role_id'))
|
role_test = PermissionsServers.get_role_servers_from_role_id(
|
||||||
for t in role_test:
|
role.get("role_id")
|
||||||
role_server.append(t)
|
)
|
||||||
|
for test in role_test:
|
||||||
|
role_server.append(test)
|
||||||
|
|
||||||
for s in role_server:
|
for server in role_server:
|
||||||
authorized_servers.append(servers_helper.get_server_data_by_id(s.server_id))
|
authorized_servers.append(
|
||||||
|
HelperServers.get_server_data_by_id(server.server_id)
|
||||||
|
)
|
||||||
|
|
||||||
for s in authorized_servers:
|
for server in authorized_servers:
|
||||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
|
||||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]})
|
server_data.append(
|
||||||
|
{
|
||||||
|
"server_data": server,
|
||||||
|
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||||
|
}
|
||||||
|
)
|
||||||
return server_data
|
return server_data
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from app.classes.controllers.roles_controller import Roles_Controller
|
from app.classes.controllers.roles_controller import RolesController
|
||||||
from app.classes.models.servers import servers_helper
|
from app.classes.models.servers import HelperServers
|
||||||
from app.classes.models.users import users_helper, ApiKeys
|
from app.classes.models.server_stats import HelperServerStats
|
||||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
from app.classes.models.users import HelperUsers, ApiKeys
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.models.server_permissions import (
|
||||||
from app.classes.shared.main_models import db_helper
|
PermissionsServers,
|
||||||
|
EnumPermissionsServer,
|
||||||
|
)
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Servers_Controller:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class ServersController:
|
||||||
|
def __init__(self, servers_helper):
|
||||||
|
self.servers_helper = servers_helper
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# Generic Servers Methods
|
# Generic Servers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
|
||||||
def create_server(
|
def create_server(
|
||||||
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
server_uuid: str,
|
server_uuid: str,
|
||||||
server_dir: str,
|
server_dir: str,
|
||||||
@ -27,8 +35,31 @@ class Servers_Controller:
|
|||||||
server_log_file: str,
|
server_log_file: str,
|
||||||
server_stop: str,
|
server_stop: str,
|
||||||
server_type: str,
|
server_type: str,
|
||||||
server_port=25565):
|
server_port: int = 25565,
|
||||||
return servers_helper.create_server(
|
server_host: str = "127.0.0.1",
|
||||||
|
) -> int:
|
||||||
|
"""Create a server in the database
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the server
|
||||||
|
server_uuid: This is the UUID of the server
|
||||||
|
server_dir: The directory where the server is located
|
||||||
|
backup_path: The path to the backup folder
|
||||||
|
server_command: The command to start the server
|
||||||
|
server_file: The name of the server file
|
||||||
|
server_log_file: The path to the server log file
|
||||||
|
server_stop: This is the command to stop the server
|
||||||
|
server_type: This is the type of server you're creating.
|
||||||
|
server_port: The port the server will be monitored on, defaults to 25565
|
||||||
|
server_host: The host the server will be monitored on, defaults to 127.0.0.1
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The new server's id
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
PeeweeException: If the server already exists
|
||||||
|
"""
|
||||||
|
return HelperServers.create_server(
|
||||||
name,
|
name,
|
||||||
server_uuid,
|
server_uuid,
|
||||||
server_dir,
|
server_dir,
|
||||||
@ -38,199 +69,235 @@ class Servers_Controller:
|
|||||||
server_log_file,
|
server_log_file,
|
||||||
server_stop,
|
server_stop,
|
||||||
server_type,
|
server_type,
|
||||||
server_port)
|
server_port,
|
||||||
|
server_host,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_obj(server_id):
|
def get_server_obj(server_id):
|
||||||
return servers_helper.get_server_obj(server_id)
|
return HelperServers.get_server_obj(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_server(server_obj):
|
def update_server(server_obj):
|
||||||
return servers_helper.update_server(server_obj)
|
return HelperServers.update_server(server_obj)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_download(server_id):
|
def set_download(server_id):
|
||||||
return servers_helper.set_download(server_id)
|
return HelperServerStats.set_download(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def finish_download(server_id):
|
def finish_download(server_id):
|
||||||
return servers_helper.finish_download(server_id)
|
return HelperServerStats.finish_download(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_download_status(server_id):
|
def get_download_status(server_id):
|
||||||
return servers_helper.get_download_status(server_id)
|
return HelperServerStats.get_download_status(server_id)
|
||||||
|
|
||||||
@staticmethod
|
def remove_server(self, server_id):
|
||||||
def remove_server(server_id):
|
roles_list = PermissionsServers.get_roles_from_server(server_id)
|
||||||
roles_list = server_permissions.get_roles_from_server(server_id)
|
|
||||||
for role in roles_list:
|
for role in roles_list:
|
||||||
role_id = role.role_id
|
role_id = role.role_id
|
||||||
role_data = Roles_Controller.get_role_with_servers(role_id)
|
role_data = RolesController.get_role_with_servers(role_id)
|
||||||
role_data['servers'] = {server_id}
|
role_data["servers"] = {server_id}
|
||||||
server_permissions.delete_roles_permissions(role_id, role_data['servers'])
|
PermissionsServers.delete_roles_permissions(role_id, role_data["servers"])
|
||||||
server_permissions.remove_roles_of_server(server_id)
|
PermissionsServers.remove_roles_of_server(server_id)
|
||||||
servers_helper.remove_server(server_id)
|
self.servers_helper.remove_server(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_data_by_id(server_id):
|
def get_server_data_by_id(server_id):
|
||||||
return servers_helper.get_server_data_by_id(server_id)
|
return HelperServers.get_server_data_by_id(server_id)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Methods
|
# Servers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_defined_servers():
|
def get_all_defined_servers():
|
||||||
return servers_helper.get_all_defined_servers()
|
return HelperServers.get_all_defined_servers()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_authorized_servers(user_id):
|
def get_authorized_servers(user_id):
|
||||||
server_data = []
|
server_data: t.List[t.Dict[str, t.Any]] = []
|
||||||
user_roles = users_helper.user_role_query(user_id)
|
user_roles = HelperUsers.user_role_query(user_id)
|
||||||
for us in user_roles:
|
for user in user_roles:
|
||||||
role_servers = server_permissions.get_role_servers_from_role_id(us.role_id)
|
role_servers = PermissionsServers.get_role_servers_from_role_id(
|
||||||
|
user.role_id
|
||||||
|
)
|
||||||
for role in role_servers:
|
for role in role_servers:
|
||||||
server_data.append(servers_helper.get_server_data_by_id(role.server_id))
|
server_data.append(HelperServers.get_server_data_by_id(role.server_id))
|
||||||
|
|
||||||
return server_data
|
return server_data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_authorized_users(server_id: str):
|
||||||
|
user_ids: t.Set[int] = set()
|
||||||
|
roles_list = PermissionsServers.get_roles_from_server(server_id)
|
||||||
|
for role in roles_list:
|
||||||
|
role_users = HelperUsers.get_users_from_role(role.role_id)
|
||||||
|
for user_role in role_users:
|
||||||
|
user_ids.add(user_role.user_id)
|
||||||
|
|
||||||
|
for user_id in HelperUsers.get_super_user_list():
|
||||||
|
user_ids.add(user_id)
|
||||||
|
|
||||||
|
return user_ids
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_servers_stats():
|
def get_all_servers_stats():
|
||||||
return servers_helper.get_all_servers_stats()
|
return HelperServerStats.get_all_servers_stats()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
|
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
|
||||||
server_data = []
|
server_data = []
|
||||||
authorized_servers = Servers_Controller.get_authorized_servers(api_key.user.user_id)
|
authorized_servers = ServersController.get_authorized_servers(
|
||||||
|
api_key.user.user_id # TODO: API key authorized servers?
|
||||||
|
)
|
||||||
|
|
||||||
for s in authorized_servers:
|
for server in authorized_servers:
|
||||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
|
||||||
key_permissions = server_permissions.get_api_key_permissions_list(api_key, s.get('server_id'))
|
key_permissions = PermissionsServers.get_api_key_permissions_list(
|
||||||
if Enum_Permissions_Server.Commands in key_permissions:
|
api_key, server.get("server_id")
|
||||||
|
)
|
||||||
|
if EnumPermissionsServer.COMMANDS in key_permissions:
|
||||||
user_command_permission = True
|
user_command_permission = True
|
||||||
else:
|
else:
|
||||||
user_command_permission = False
|
user_command_permission = False
|
||||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0],
|
server_data.append(
|
||||||
"user_command_permission": user_command_permission})
|
{
|
||||||
|
"server_data": server,
|
||||||
|
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||||
|
"user_command_permission": user_command_permission,
|
||||||
|
}
|
||||||
|
)
|
||||||
return server_data
|
return server_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_authorized_servers_stats(user_id):
|
def get_authorized_servers_stats(user_id):
|
||||||
server_data = []
|
server_data = []
|
||||||
authorized_servers = Servers_Controller.get_authorized_servers(user_id)
|
authorized_servers = ServersController.get_authorized_servers(user_id)
|
||||||
|
|
||||||
for s in authorized_servers:
|
for server in authorized_servers:
|
||||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
|
||||||
# TODO
|
# TODO
|
||||||
user_permissions = server_permissions.get_user_id_permissions_list(user_id, s.get('server_id'))
|
user_permissions = PermissionsServers.get_user_id_permissions_list(
|
||||||
if Enum_Permissions_Server.Commands in user_permissions:
|
user_id, server.get("server_id")
|
||||||
|
)
|
||||||
|
if EnumPermissionsServer.COMMANDS in user_permissions:
|
||||||
user_command_permission = True
|
user_command_permission = True
|
||||||
else:
|
else:
|
||||||
user_command_permission = False
|
user_command_permission = False
|
||||||
server_data.append({
|
server_data.append(
|
||||||
'server_data': s,
|
{
|
||||||
'stats': db_helper.return_rows(latest)[0],
|
"server_data": server,
|
||||||
'user_command_permission': user_command_permission
|
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||||
})
|
"user_command_permission": user_command_permission,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return server_data
|
return server_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_friendly_name(server_id):
|
def get_server_friendly_name(server_id):
|
||||||
return servers_helper.get_server_friendly_name(server_id)
|
return HelperServers.get_server_friendly_name(server_id)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers_Stats Methods
|
# Servers_Stats Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_stats_by_id(server_id):
|
def get_server_stats_by_id(server_id):
|
||||||
return servers_helper.get_server_stats_by_id(server_id)
|
return HelperServerStats.get_server_stats_by_id(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def server_id_exists(server_id):
|
def server_id_exists(server_id):
|
||||||
return servers_helper.server_id_exists(server_id)
|
return HelperServerStats.server_id_exists(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_type_by_id(server_id):
|
def get_server_type_by_id(server_id):
|
||||||
return servers_helper.get_server_type_by_id(server_id)
|
return HelperServers.get_server_type_by_id(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def server_id_authorized(server_id_a, user_id):
|
def server_id_authorized(server_id_a, user_id):
|
||||||
user_roles = users_helper.user_role_query(user_id)
|
user_roles = HelperUsers.user_role_query(user_id)
|
||||||
for role in user_roles:
|
for role in user_roles:
|
||||||
for server_id_b in server_permissions.get_role_servers_from_role_id(role.role_id):
|
for server_id_b in PermissionsServers.get_role_servers_from_role_id(
|
||||||
|
role.role_id
|
||||||
|
):
|
||||||
if str(server_id_a) == str(server_id_b.server_id):
|
if str(server_id_a) == str(server_id_b.server_id):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_crashed(server_id):
|
def is_crashed(server_id):
|
||||||
return servers_helper.is_crashed(server_id)
|
return HelperServerStats.is_crashed(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
|
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
|
||||||
# TODO
|
# TODO
|
||||||
return Servers_Controller.server_id_authorized(server_id, api_key.user.user_id)
|
return ServersController.server_id_authorized(server_id, api_key.user.user_id)
|
||||||
# There is no view server permission
|
# There is no view server permission
|
||||||
# permission_helper.both_have_perm(api_key)
|
# permission_helper.both_have_perm(api_key)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_update(server_id, value):
|
def set_update(server_id, value):
|
||||||
return servers_helper.set_update(server_id, value)
|
return HelperServerStats.set_update(server_id, value)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_TTL_without_player(server_id):
|
def get_ttl_without_player(server_id):
|
||||||
return servers_helper.get_TTL_without_player(server_id)
|
return HelperServerStats.get_ttl_without_player(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_stop_no_players(server_id, time_limit):
|
def can_stop_no_players(server_id, time_limit):
|
||||||
return servers_helper.can_stop_no_players(server_id, time_limit)
|
return HelperServerStats.can_stop_no_players(server_id, time_limit)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_waiting_start(server_id, value):
|
def set_waiting_start(server_id, value):
|
||||||
servers_helper.set_waiting_start(server_id, value)
|
HelperServerStats.set_waiting_start(server_id, value)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_waiting_start(server_id):
|
def get_waiting_start(server_id):
|
||||||
return servers_helper.get_waiting_start(server_id)
|
return HelperServerStats.get_waiting_start(server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_update_status(server_id):
|
def get_update_status(server_id):
|
||||||
return servers_helper.get_update_status(server_id)
|
return HelperServerStats.get_update_status(server_id)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Helpers Methods
|
# Servers Helpers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_banned_players(server_id):
|
def get_banned_players(server_id):
|
||||||
stats = servers_helper.get_server_stats_by_id(server_id)
|
stats = HelperServerStats.get_server_stats_by_id(server_id)
|
||||||
server_path = stats['server_id']['path']
|
server_path = stats["server_id"]["path"]
|
||||||
path = os.path.join(server_path, 'banned-players.json')
|
path = os.path.join(server_path, "banned-players.json")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(helper.get_os_understandable_path(path), encoding='utf-8') as file:
|
with open(
|
||||||
|
Helpers.get_os_understandable_path(path), encoding="utf-8"
|
||||||
|
) as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
file.close()
|
file.close()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print (ex)
|
print(ex)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return json.loads(content)
|
return json.loads(content)
|
||||||
|
|
||||||
def check_for_old_logs(self):
|
def check_for_old_logs(self):
|
||||||
servers = servers_helper.get_all_defined_servers()
|
servers = HelperServers.get_all_defined_servers()
|
||||||
for server in servers:
|
for server in servers:
|
||||||
logs_path = os.path.split(server['log_path'])[0]
|
logs_path = os.path.split(server["log_path"])[0]
|
||||||
latest_log_file = os.path.split(server['log_path'])[1]
|
latest_log_file = os.path.split(server["log_path"])[1]
|
||||||
logs_delete_after = int(server['logs_delete_after'])
|
logs_delete_after = int(server["logs_delete_after"])
|
||||||
if logs_delete_after == 0:
|
if logs_delete_after == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
log_files = list(filter(
|
log_files = list(
|
||||||
lambda val: val != latest_log_file,
|
filter(lambda val: val != latest_log_file, os.listdir(logs_path))
|
||||||
os.listdir(logs_path)
|
)
|
||||||
))
|
|
||||||
for log_file in log_files:
|
for log_file in log_files:
|
||||||
log_file_path = os.path.join(logs_path, log_file)
|
log_file_path = os.path.join(logs_path, log_file)
|
||||||
if helper.check_file_exists(log_file_path) and \
|
if Helpers.check_file_exists(
|
||||||
helper.is_file_older_than_x_days(log_file_path, logs_delete_after):
|
log_file_path
|
||||||
|
) and Helpers.is_file_older_than_x_days(
|
||||||
|
log_file_path, logs_delete_after
|
||||||
|
):
|
||||||
os.remove(log_file_path)
|
os.remove(log_file_path)
|
||||||
|
@ -1,57 +1,134 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
import typing as t
|
||||||
|
|
||||||
from app.classes.models.users import users_helper
|
from app.classes.models.users import HelperUsers
|
||||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
from app.classes.models.crafty_permissions import (
|
||||||
from app.classes.shared.helpers import helper
|
PermissionsCrafty,
|
||||||
from app.classes.shared.authentication import authentication
|
EnumPermissionsCrafty,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Users_Controller:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class UsersController:
|
||||||
|
def __init__(self, helper, users_helper, authentication):
|
||||||
|
self.helper = helper
|
||||||
|
self.users_helper = users_helper
|
||||||
|
self.authentication = authentication
|
||||||
|
|
||||||
|
_permissions_props = {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
permission.name
|
||||||
|
for permission in PermissionsCrafty.get_permissions_list()
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"quantity": {"type": "number", "minimum": 0},
|
||||||
|
"enabled": {"type": "boolean"},
|
||||||
|
}
|
||||||
|
self.user_jsonschema_props: t.Final = {
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 20,
|
||||||
|
"minLength": 4,
|
||||||
|
"pattern": "^[a-z0-9_]+$",
|
||||||
|
"examples": ["admin"],
|
||||||
|
"title": "Username",
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 20,
|
||||||
|
"minLength": 4,
|
||||||
|
"examples": ["crafty"],
|
||||||
|
"title": "Password",
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "email",
|
||||||
|
"examples": ["default@example.com"],
|
||||||
|
"title": "E-Mail",
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"examples": [True],
|
||||||
|
"title": "Enabled",
|
||||||
|
},
|
||||||
|
"lang": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 10,
|
||||||
|
"minLength": 2,
|
||||||
|
"examples": ["en"],
|
||||||
|
"title": "Language",
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"type": "boolean",
|
||||||
|
"examples": [False],
|
||||||
|
"title": "Superuser",
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": _permissions_props,
|
||||||
|
"required": ["name", "quantity", "enabled"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"hints": {"type": "boolean"},
|
||||||
|
}
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# Users Methods
|
# Users Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_users():
|
def get_all_users():
|
||||||
return users_helper.get_all_users()
|
return HelperUsers.get_all_users()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_user_ids() -> t.List[int]:
|
||||||
|
return HelperUsers.get_all_user_ids()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id_by_name(username):
|
def get_id_by_name(username):
|
||||||
return users_helper.get_user_id_by_name(username)
|
return HelperUsers.get_user_id_by_name(username)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_lang_by_id(user_id):
|
def get_user_lang_by_id(user_id):
|
||||||
return users_helper.get_user_lang_by_id(user_id)
|
return HelperUsers.get_user_lang_by_id(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_by_id(user_id):
|
def get_user_by_id(user_id):
|
||||||
return users_helper.get_user(user_id)
|
return HelperUsers.get_user(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_server_order(user_id, user_server_order):
|
def update_server_order(user_id, user_server_order):
|
||||||
users_helper.update_server_order(user_id, user_server_order)
|
HelperUsers.update_server_order(user_id, user_server_order)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_order(user_id):
|
def get_server_order(user_id):
|
||||||
return users_helper.get_server_order(user_id)
|
return HelperUsers.get_server_order(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_query(user_id):
|
def user_query(user_id):
|
||||||
return users_helper.user_query(user_id)
|
return HelperUsers.user_query(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_support_path(user_id, support_path):
|
def set_support_path(user_id, support_path):
|
||||||
users_helper.set_support_path(user_id, support_path)
|
HelperUsers.set_support_path(user_id, support_path)
|
||||||
|
|
||||||
@staticmethod
|
def update_user(self, user_id: str, user_data=None, user_crafty_data=None):
|
||||||
def update_user(user_id: str, user_data=None, user_crafty_data=None):
|
|
||||||
if user_crafty_data is None:
|
if user_crafty_data is None:
|
||||||
user_crafty_data = {}
|
user_crafty_data = {}
|
||||||
if user_data is None:
|
if user_data is None:
|
||||||
user_data = {}
|
user_data = {}
|
||||||
base_data = users_helper.get_user(user_id)
|
base_data = HelperUsers.get_user(user_id)
|
||||||
up_data = {}
|
up_data = {}
|
||||||
added_roles = set()
|
added_roles = set()
|
||||||
removed_roles = set()
|
removed_roles = set()
|
||||||
@ -59,110 +136,171 @@ class Users_Controller:
|
|||||||
if key == "user_id":
|
if key == "user_id":
|
||||||
continue
|
continue
|
||||||
elif key == "roles":
|
elif key == "roles":
|
||||||
added_roles = user_data['roles'].difference(base_data['roles'])
|
added_roles = set(user_data["roles"]).difference(
|
||||||
removed_roles = base_data['roles'].difference(user_data['roles'])
|
set(base_data["roles"])
|
||||||
|
)
|
||||||
|
removed_roles = set(base_data["roles"]).difference(
|
||||||
|
set(user_data["roles"])
|
||||||
|
)
|
||||||
elif key == "password":
|
elif key == "password":
|
||||||
if user_data['password'] is not None and user_data['password'] != "":
|
if user_data["password"] is not None and user_data["password"] != "":
|
||||||
up_data['password'] = helper.encode_pass(user_data['password'])
|
up_data["password"] = self.helper.encode_pass(user_data["password"])
|
||||||
|
elif key == "lang":
|
||||||
|
up_data["lang"] = user_data["lang"]
|
||||||
|
elif key == "hints":
|
||||||
|
up_data["hints"] = user_data["hints"]
|
||||||
elif base_data[key] != user_data[key]:
|
elif base_data[key] != user_data[key]:
|
||||||
up_data[key] = user_data[key]
|
up_data[key] = user_data[key]
|
||||||
up_data['last_update'] = helper.get_time_as_string()
|
up_data["last_update"] = self.helper.get_time_as_string()
|
||||||
up_data['lang'] = user_data['lang']
|
|
||||||
logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}")
|
logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}")
|
||||||
for role in added_roles:
|
for role in added_roles:
|
||||||
users_helper.get_or_create(user_id=user_id, role_id=role)
|
HelperUsers.get_or_create(user_id=user_id, role_id=role)
|
||||||
permissions_mask = user_crafty_data.get('permissions_mask', '000')
|
permissions_mask = user_crafty_data.get("permissions_mask", "000")
|
||||||
|
|
||||||
if 'server_quantity' in user_crafty_data:
|
if "server_quantity" in user_crafty_data:
|
||||||
limit_server_creation = user_crafty_data['server_quantity'][
|
limit_server_creation = user_crafty_data["server_quantity"].get(
|
||||||
Enum_Permissions_Crafty.Server_Creation.name]
|
EnumPermissionsCrafty.SERVER_CREATION.name, 0
|
||||||
|
)
|
||||||
|
|
||||||
limit_user_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.User_Config.name]
|
limit_user_creation = user_crafty_data["server_quantity"].get(
|
||||||
limit_role_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Roles_Config.name]
|
EnumPermissionsCrafty.USER_CONFIG.name, 0
|
||||||
else:
|
)
|
||||||
limit_server_creation = 0
|
limit_role_creation = user_crafty_data["server_quantity"].get(
|
||||||
limit_user_creation = 0
|
EnumPermissionsCrafty.ROLES_CONFIG.name, 0
|
||||||
limit_role_creation = 0
|
)
|
||||||
|
else:
|
||||||
|
limit_server_creation = 0
|
||||||
|
limit_user_creation = 0
|
||||||
|
limit_role_creation = 0
|
||||||
|
|
||||||
crafty_permissions.add_or_update_user(
|
PermissionsCrafty.add_or_update_user(
|
||||||
user_id,
|
user_id,
|
||||||
permissions_mask,
|
permissions_mask,
|
||||||
limit_server_creation,
|
limit_server_creation,
|
||||||
limit_user_creation,
|
limit_user_creation,
|
||||||
limit_role_creation)
|
limit_role_creation,
|
||||||
|
)
|
||||||
|
|
||||||
users_helper.delete_user_roles(user_id, removed_roles)
|
self.users_helper.delete_user_roles(user_id, removed_roles)
|
||||||
|
|
||||||
users_helper.update_user(user_id, up_data)
|
self.users_helper.update_user(user_id, up_data)
|
||||||
|
|
||||||
|
def raw_update_user(self, user_id: int, up_data: t.Optional[t.Dict[str, t.Any]]):
|
||||||
|
"""Directly passes the data to the model helper.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (int): The id of the user to update.
|
||||||
|
up_data (t.Optional[t.Dict[str, t.Any]]): Update data.
|
||||||
|
"""
|
||||||
|
self.users_helper.update_user(user_id, up_data)
|
||||||
|
|
||||||
|
def add_user(
|
||||||
|
self,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
email="default@example.com",
|
||||||
|
enabled: bool = True,
|
||||||
|
superuser: bool = False,
|
||||||
|
):
|
||||||
|
return self.users_helper.add_user(
|
||||||
|
username,
|
||||||
|
password=password,
|
||||||
|
email=email,
|
||||||
|
enabled=enabled,
|
||||||
|
superuser=superuser,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_user(username, password=None, email="default@example.com", enabled: bool = True, superuser: bool = False):
|
def add_rawpass_user(
|
||||||
return users_helper.add_user(username, password=password, email=email, enabled=enabled, superuser=superuser)
|
username,
|
||||||
|
password,
|
||||||
|
email="default@example.com",
|
||||||
|
enabled: bool = True,
|
||||||
|
superuser: bool = False,
|
||||||
|
):
|
||||||
|
return HelperUsers.add_rawpass_user(
|
||||||
|
username,
|
||||||
|
password=password,
|
||||||
|
email=email,
|
||||||
|
enabled=enabled,
|
||||||
|
superuser=superuser,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
def remove_user(self, user_id):
|
||||||
def remove_user(user_id):
|
return self.users_helper.remove_user(user_id)
|
||||||
return users_helper.remove_user(user_id)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_id_exists(user_id):
|
def user_id_exists(user_id):
|
||||||
return users_helper.user_id_exists(user_id)
|
return HelperUsers.user_id_exists(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_id_by_api_token(token: str) -> str:
|
def set_prepare(user_id):
|
||||||
token_data = authentication.check_no_iat(token)
|
return HelperUsers.set_prepare(user_id)
|
||||||
return token_data['user_id']
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_by_api_token(token: str):
|
def stop_prepare(user_id):
|
||||||
_, user = authentication.check(token)
|
return HelperUsers.stop_prepare(user_id)
|
||||||
|
|
||||||
|
def get_user_id_by_api_token(self, token: str) -> str:
|
||||||
|
token_data = self.authentication.check_no_iat(token)
|
||||||
|
return token_data["user_id"]
|
||||||
|
|
||||||
|
def get_user_by_api_token(self, token: str):
|
||||||
|
_, _, user = self.authentication.check_err(token)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
# ************************************************************************************************
|
def get_api_key_by_token(self, token: str):
|
||||||
|
key, _, _ = self.authentication.check(token)
|
||||||
|
return key
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# User Roles Methods
|
# User Roles Methods
|
||||||
# ************************************************************************************************
|
# **********************************************************************************
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_roles_id(user_id):
|
def get_user_roles_id(user_id):
|
||||||
return users_helper.get_user_roles_id(user_id)
|
return HelperUsers.get_user_roles_id(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_roles_names(user_id):
|
def get_user_roles_names(user_id):
|
||||||
return users_helper.get_user_roles_names(user_id)
|
return HelperUsers.get_user_roles_names(user_id)
|
||||||
|
|
||||||
@staticmethod
|
def add_role_to_user(self, user_id, role_id):
|
||||||
def add_role_to_user(user_id, role_id):
|
return self.users_helper.add_role_to_user(user_id, role_id)
|
||||||
return users_helper.add_role_to_user(user_id, role_id)
|
|
||||||
|
|
||||||
@staticmethod
|
def add_user_roles(self, user):
|
||||||
def add_user_roles(user):
|
return self.users_helper.add_user_roles(user)
|
||||||
return users_helper.add_user_roles(user)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_role_query(user_id):
|
def user_role_query(user_id):
|
||||||
return users_helper.user_role_query(user_id)
|
return HelperUsers.user_role_query(user_id)
|
||||||
|
|
||||||
# ************************************************************************************************
|
# **********************************************************************************
|
||||||
# Api Keys Methods
|
# Api Keys Methods
|
||||||
# ************************************************************************************************
|
# **********************************************************************************
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_api_keys(user_id: str):
|
def get_user_api_keys(user_id: str):
|
||||||
return users_helper.get_user_api_keys(user_id)
|
return HelperUsers.get_user_api_keys(user_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_api_key(key_id: str):
|
def get_user_api_key(key_id: str):
|
||||||
return users_helper.get_user_api_key(key_id)
|
return HelperUsers.get_user_api_key(key_id)
|
||||||
|
|
||||||
@staticmethod
|
def add_user_api_key(
|
||||||
def add_user_api_key(name: str, user_id: str, superuser: bool = False,
|
self,
|
||||||
server_permissions_mask: Optional[str] = None,
|
name: str,
|
||||||
crafty_permissions_mask: Optional[str] = None):
|
user_id: str,
|
||||||
return users_helper.add_user_api_key(name, user_id, superuser, server_permissions_mask, crafty_permissions_mask)
|
superuser: bool = False,
|
||||||
|
server_permissions_mask: t.Optional[str] = None,
|
||||||
|
crafty_permissions_mask: t.Optional[str] = None,
|
||||||
|
):
|
||||||
|
return self.users_helper.add_user_api_key(
|
||||||
|
name, user_id, superuser, server_permissions_mask, crafty_permissions_mask
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
def delete_user_api_keys(self, user_id: str):
|
||||||
def delete_user_api_keys(user_id: str):
|
return self.users_helper.delete_user_api_keys(user_id)
|
||||||
return users_helper.delete_user_api_keys(user_id)
|
|
||||||
|
|
||||||
@staticmethod
|
def delete_user_api_key(self, key_id: str):
|
||||||
def delete_user_api_key(key_id: str):
|
return self.users_helper.delete_user_api_key(key_id)
|
||||||
return users_helper.delete_user_api_key(key_id)
|
|
||||||
|
@ -3,21 +3,22 @@ import socket
|
|||||||
import time
|
import time
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
|
|
||||||
class BedrockPing:
|
class BedrockPing:
|
||||||
magic = b'\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78'
|
magic = b"\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"
|
||||||
fields = { # (len, signed)
|
fields = { # (len, signed)
|
||||||
"byte": (1, False),
|
"byte": (1, False),
|
||||||
"long": (8, True),
|
"long": (8, True),
|
||||||
"ulong": (8, False),
|
"ulong": (8, False),
|
||||||
"magic": (16, False),
|
"magic": (16, False),
|
||||||
"short": (2, True),
|
"short": (2, True),
|
||||||
"ushort": (2, False), #unsigned short
|
"ushort": (2, False), # unsigned short
|
||||||
"string": (2, False), #strlen is ushort
|
"string": (2, False), # strlen is ushort
|
||||||
"bool": (1, False),
|
"bool": (1, False),
|
||||||
"address": (7, False),
|
"address": (7, False),
|
||||||
"uint24le": (3, False)
|
"uint24le": (3, False),
|
||||||
}
|
}
|
||||||
byte_order = 'big'
|
byte_order = "big"
|
||||||
|
|
||||||
def __init__(self, bedrock_addr, bedrock_port, client_guid=0, timeout=5):
|
def __init__(self, bedrock_addr, bedrock_port, client_guid=0, timeout=5):
|
||||||
self.addr = bedrock_addr
|
self.addr = bedrock_addr
|
||||||
@ -36,51 +37,72 @@ class BedrockPing:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def __slice(in_bytes, pattern):
|
def __slice(in_bytes, pattern):
|
||||||
ret = []
|
ret = []
|
||||||
bi = 0 # bytes index
|
bytes_index = 0
|
||||||
pi = 0 # pattern index
|
pattern_index = 0
|
||||||
while bi < len(in_bytes):
|
while bytes_index < len(in_bytes):
|
||||||
try:
|
try:
|
||||||
f = BedrockPing.fields[pattern[pi]]
|
field = BedrockPing.fields[pattern[pattern_index]]
|
||||||
except IndexError as index_error:
|
except IndexError as index_error:
|
||||||
raise IndexError("Ran out of pattern with additional bytes remaining") from index_error
|
raise IndexError(
|
||||||
if pattern[pi] == "string":
|
"Ran out of pattern with additional bytes remaining"
|
||||||
shl = f[0] # string header length
|
) from index_error
|
||||||
sl = int.from_bytes(in_bytes[bi:bi+shl], BedrockPing.byte_order, signed=f[1]) # string length
|
if pattern[pattern_index] == "string":
|
||||||
l = shl+sl
|
string_header_length = field[0]
|
||||||
ret.append(in_bytes[bi+shl:bi+shl+sl].decode('ascii'))
|
string_length = int.from_bytes(
|
||||||
elif pattern[pi] == "magic":
|
in_bytes[bytes_index : bytes_index + string_header_length],
|
||||||
l = f[0] # length of field
|
BedrockPing.byte_order,
|
||||||
ret.append(in_bytes[bi:bi+l])
|
signed=field[1],
|
||||||
|
)
|
||||||
|
length = string_header_length + string_length
|
||||||
|
ret.append(
|
||||||
|
in_bytes[
|
||||||
|
bytes_index
|
||||||
|
+ string_header_length : bytes_index
|
||||||
|
+ string_header_length
|
||||||
|
+ string_length
|
||||||
|
].decode("ascii")
|
||||||
|
)
|
||||||
|
elif pattern[pattern_index] == "magic":
|
||||||
|
length = field[0]
|
||||||
|
ret.append(in_bytes[bytes_index : bytes_index + length])
|
||||||
else:
|
else:
|
||||||
l = f[0] # length of field
|
length = field[0]
|
||||||
ret.append(int.from_bytes(in_bytes[bi:bi+l], BedrockPing.byte_order, signed=f[1]))
|
ret.append(
|
||||||
bi+=l
|
int.from_bytes(
|
||||||
pi+=1
|
in_bytes[bytes_index : bytes_index + length],
|
||||||
|
BedrockPing.byte_order,
|
||||||
|
signed=field[1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
bytes_index += length
|
||||||
|
pattern_index += 1
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_time():
|
def __get_time():
|
||||||
#return time.time_ns() // 1000000
|
# return time.time_ns() // 1000000
|
||||||
return time.perf_counter_ns() // 1000000
|
return time.perf_counter_ns() // 1000000
|
||||||
|
|
||||||
def __sendping(self):
|
def __sendping(self):
|
||||||
pack_id = BedrockPing.__byter(0x01, 'byte')
|
pack_id = BedrockPing.__byter(0x01, "byte")
|
||||||
now = BedrockPing.__byter(BedrockPing.__get_time(), 'ulong')
|
now = BedrockPing.__byter(BedrockPing.__get_time(), "ulong")
|
||||||
guid = self.guid_bytes
|
guid = self.guid_bytes
|
||||||
d2s = pack_id+now+BedrockPing.magic+guid
|
d2s = pack_id + now + BedrockPing.magic + guid
|
||||||
#print("S:", d2s)
|
# print("S:", d2s)
|
||||||
self.sock.sendto(d2s, (self.addr, self.port))
|
self.sock.sendto(d2s, (self.addr, self.port))
|
||||||
|
|
||||||
def __recvpong(self):
|
def __recvpong(self):
|
||||||
data = self.sock.recv(4096)
|
data = self.sock.recv(4096)
|
||||||
if data[0] == 0x1c:
|
if data[0] == 0x1C:
|
||||||
ret = {}
|
ret = {}
|
||||||
sliced = BedrockPing.__slice(data,["byte","ulong","ulong","magic","string"])
|
sliced = BedrockPing.__slice(
|
||||||
|
data, ["byte", "ulong", "ulong", "magic", "string"]
|
||||||
|
)
|
||||||
if sliced[3] != BedrockPing.magic:
|
if sliced[3] != BedrockPing.magic:
|
||||||
raise ValueError(f"Incorrect magic received ({sliced[3]})")
|
raise ValueError(f"Incorrect magic received ({sliced[3]})")
|
||||||
ret["server_guid"] = sliced[2]
|
ret["server_guid"] = sliced[2]
|
||||||
ret["server_string_raw"] = sliced[4]
|
ret["server_string_raw"] = sliced[4]
|
||||||
server_info = sliced[4].split(';')
|
server_info = sliced[4].split(";")
|
||||||
ret["server_edition"] = server_info[0]
|
ret["server_edition"] = server_info[0]
|
||||||
ret["server_motd"] = (server_info[1], server_info[7])
|
ret["server_motd"] = (server_info[1], server_info[7])
|
||||||
ret["server_protocol_version"] = server_info[2]
|
ret["server_protocol_version"] = server_info[2]
|
||||||
@ -103,5 +125,7 @@ class BedrockPing:
|
|||||||
self.__sendping()
|
self.__sendping()
|
||||||
return self.__recvpong()
|
return self.__recvpong()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}")
|
print(
|
||||||
|
f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}"
|
||||||
|
)
|
||||||
rtr -= 1
|
rtr -= 1
|
||||||
|
@ -9,28 +9,29 @@ import uuid
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from app.classes.minecraft.bedrock_ping import BedrockPing
|
from app.classes.minecraft.bedrock_ping import BedrockPing
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import Console
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.description = data.get('description')
|
self.description = data.get("description")
|
||||||
# print(self.description)
|
# print(self.description)
|
||||||
if isinstance(self.description, dict):
|
if isinstance(self.description, dict):
|
||||||
|
|
||||||
# cat server
|
# cat server
|
||||||
if "translate" in self.description:
|
if "translate" in self.description:
|
||||||
self.description = self.description['translate']
|
self.description = self.description["translate"]
|
||||||
|
|
||||||
# waterfall / bungee
|
# waterfall / bungee
|
||||||
elif 'extra' in self.description:
|
elif "extra" in self.description:
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
description = self.description
|
description = self.description
|
||||||
if 'extra' in description.keys():
|
if "extra" in description.keys():
|
||||||
for e in description['extra']:
|
for e in description["extra"]:
|
||||||
#Conversion format code needed only for Java Version
|
# Conversion format code needed only for Java Version
|
||||||
lines.append(get_code_format("reset"))
|
lines.append(get_code_format("reset"))
|
||||||
if "bold" in e.keys():
|
if "bold" in e.keys():
|
||||||
lines.append(get_code_format("bold"))
|
lines.append(get_code_format("bold"))
|
||||||
@ -43,77 +44,76 @@ class Server:
|
|||||||
if "obfuscated" in e.keys():
|
if "obfuscated" in e.keys():
|
||||||
lines.append(get_code_format("obfuscated"))
|
lines.append(get_code_format("obfuscated"))
|
||||||
if "color" in e.keys():
|
if "color" in e.keys():
|
||||||
lines.append(get_code_format(e['color']))
|
lines.append(get_code_format(e["color"]))
|
||||||
#Then append the text
|
# Then append the text
|
||||||
if "text" in e.keys():
|
if "text" in e.keys():
|
||||||
if e['text'] == '\n':
|
if e["text"] == "\n":
|
||||||
lines.append("§§")
|
lines.append("§§")
|
||||||
else:
|
else:
|
||||||
lines.append(e['text'])
|
lines.append(e["text"])
|
||||||
|
|
||||||
total_text = " ".join(lines)
|
total_text = " ".join(lines)
|
||||||
self.description = total_text
|
self.description = total_text
|
||||||
|
|
||||||
# normal MC
|
# normal MC
|
||||||
else:
|
else:
|
||||||
self.description = self.description['text']
|
self.description = self.description["text"]
|
||||||
|
|
||||||
self.icon = base64.b64decode(data.get('favicon', '')[22:])
|
self.icon = base64.b64decode(data.get("favicon", "")[22:])
|
||||||
try:
|
try:
|
||||||
self.players = Players(data['players']).report()
|
self.players = Players(data["players"]).report()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error("Error geting player information key error")
|
logger.error("Error geting player information key error")
|
||||||
self.players = []
|
self.players = []
|
||||||
self.version = data['version']['name']
|
self.version = data["version"]["name"]
|
||||||
self.protocol = data['version']['protocol']
|
self.protocol = data["version"]["protocol"]
|
||||||
|
|
||||||
|
|
||||||
class Players(list):
|
class Players(list):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__(Player(x) for x in data.get('sample', []))
|
super().__init__(Player(x) for x in data.get("sample", []))
|
||||||
self.max = data['max']
|
self.max = data["max"]
|
||||||
self.online = data['online']
|
self.online = data["online"]
|
||||||
|
|
||||||
def report(self):
|
def report(self):
|
||||||
players = []
|
players = []
|
||||||
|
|
||||||
for x in self:
|
for player in self:
|
||||||
players.append(str(x))
|
players.append(str(player))
|
||||||
|
|
||||||
r_data = {
|
r_data = {"online": self.online, "max": self.max, "players": players}
|
||||||
'online': self.online,
|
|
||||||
'max': self.max,
|
|
||||||
'players': players
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.dumps(r_data)
|
return json.dumps(r_data)
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.id = data['id']
|
self.id = data["id"]
|
||||||
self.name = data['name']
|
self.name = data["name"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
def get_code_format(format_name):
|
def get_code_format(format_name):
|
||||||
root_dir = os.path.abspath(os.path.curdir)
|
root_dir = os.path.abspath(os.path.curdir)
|
||||||
format_file = os.path.join(root_dir, 'app', 'config', 'motd_format.json')
|
format_file = os.path.join(root_dir, "app", "config", "motd_format.json")
|
||||||
try:
|
try:
|
||||||
with open(format_file, "r", encoding='utf-8') as f:
|
with open(format_file, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
|
||||||
if format_name in data.keys():
|
if format_name in data.keys():
|
||||||
return data.get(format_name)
|
return data.get(format_name)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Format MOTD Error: format name {format_name} does not exist")
|
logger.error(f"Format MOTD Error: format name {format_name} does not exist")
|
||||||
console.error(f"Format MOTD Error: format name {format_name} does not exist")
|
Console.error(
|
||||||
|
f"Format MOTD Error: format name {format_name} does not exist"
|
||||||
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
logger.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
||||||
console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
Console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@ -128,10 +128,10 @@ def ping(ip, port):
|
|||||||
if not k:
|
if not k:
|
||||||
return 0
|
return 0
|
||||||
k = k[0]
|
k = k[0]
|
||||||
i |= (k & 0x7f) << (j * 7)
|
i |= (k & 0x7F) << (j * 7)
|
||||||
j += 1
|
j += 1
|
||||||
if j > 5:
|
if j > 5:
|
||||||
raise ValueError('var_int too big')
|
raise ValueError("var_int too big")
|
||||||
if not k & 0x80:
|
if not k & 0x80:
|
||||||
return i
|
return i
|
||||||
|
|
||||||
@ -143,15 +143,15 @@ def ping(ip, port):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
host = ip.encode('utf-8')
|
host = ip.encode("utf-8")
|
||||||
data = b'' # wiki.vg/Server_List_Ping
|
data = b"" # wiki.vg/Server_List_Ping
|
||||||
data += b'\x00' # packet ID
|
data += b"\x00" # packet ID
|
||||||
data += b'\x04' # protocol variant
|
data += b"\x04" # protocol variant
|
||||||
data += struct.pack('>b', len(host)) + host
|
data += struct.pack(">b", len(host)) + host
|
||||||
data += struct.pack('>H', port)
|
data += struct.pack(">H", port)
|
||||||
data += b'\x01' # next state
|
data += b"\x01" # next state
|
||||||
data = struct.pack('>b', len(data)) + data
|
data = struct.pack(">b", len(data)) + data
|
||||||
sock.sendall(data + b'\x01\x00') # handshake + status ping
|
sock.sendall(data + b"\x01\x00") # handshake + status ping
|
||||||
length = read_var_int() # full packet length
|
length = read_var_int() # full packet length
|
||||||
if length < 10:
|
if length < 10:
|
||||||
if length < 0:
|
if length < 0:
|
||||||
@ -161,7 +161,7 @@ def ping(ip, port):
|
|||||||
|
|
||||||
sock.recv(1) # packet type, 0 for pings
|
sock.recv(1) # packet type, 0 for pings
|
||||||
length = read_var_int() # string length
|
length = read_var_int() # string length
|
||||||
data = b''
|
data = b""
|
||||||
while len(data) != length:
|
while len(data) != length:
|
||||||
chunk = sock.recv(length - len(data))
|
chunk = sock.recv(length - len(data))
|
||||||
if not chunk:
|
if not chunk:
|
||||||
@ -176,13 +176,14 @@ def ping(ip, port):
|
|||||||
finally:
|
finally:
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
|
|
||||||
# For the rest of requests see wiki.vg/Protocol
|
# For the rest of requests see wiki.vg/Protocol
|
||||||
def ping_bedrock(ip, port):
|
def ping_bedrock(ip, port):
|
||||||
rd = random.Random()
|
rand = random.Random()
|
||||||
try:
|
try:
|
||||||
#pylint: disable=consider-using-f-string
|
# pylint: disable=consider-using-f-string
|
||||||
rd.seed(''.join(re.findall('..', '%012x' % uuid.getnode())))
|
rand.seed("".join(re.findall("..", "%012x" % uuid.getnode())))
|
||||||
client_guid = uuid.UUID(int=rd.getrandbits(32)).int
|
client_guid = uuid.UUID(int=rand.getrandbits(32)).int
|
||||||
except:
|
except:
|
||||||
client_guid = 0
|
client_guid = 0
|
||||||
try:
|
try:
|
||||||
|
@ -1,44 +1,45 @@
|
|||||||
import pprint
|
import pprint
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class ServerProps:
|
|
||||||
|
|
||||||
|
class ServerProps:
|
||||||
def __init__(self, filepath):
|
def __init__(self, filepath):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
self.props = self._parse()
|
self.props = self._parse()
|
||||||
|
|
||||||
def _parse(self):
|
def _parse(self):
|
||||||
"""Loads and parses the file specified in self.filepath"""
|
# Loads and parses the file specified in self.filepath
|
||||||
with open(self.filepath, encoding='utf-8') as fp:
|
with open(self.filepath, encoding="utf-8") as full_path:
|
||||||
line = fp.readline()
|
line = full_path.readline()
|
||||||
d = {}
|
dictionary = {}
|
||||||
if os.path.exists(".header"):
|
if os.path.exists(".header"):
|
||||||
os.remove(".header")
|
os.remove(".header")
|
||||||
while line:
|
while line:
|
||||||
if '#' != line[0]:
|
if "#" != line[0]:
|
||||||
s = line
|
string = line
|
||||||
s1 = s[:s.find('=')]
|
string1 = string[: string.find("=")]
|
||||||
if '\n' in s:
|
if "\n" in string:
|
||||||
s2 = s[s.find('=')+1:s.find('\n')]
|
string2 = string[string.find("=") + 1 : string.find("\n")]
|
||||||
else:
|
else:
|
||||||
s2 = s[s.find('=')+1:]
|
string2 = string[string.find("=") + 1 :]
|
||||||
d[s1] = s2
|
dictionary[string1] = string2
|
||||||
else:
|
else:
|
||||||
with open(".header", "a+", encoding='utf-8') as h:
|
with open(".header", "a+", encoding="utf-8") as header:
|
||||||
h.write(line)
|
header.write(line)
|
||||||
line = fp.readline()
|
line = full_path.readline()
|
||||||
return d
|
return dictionary
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
"""Prints the properties dictionary (using pprint)"""
|
# Prints the properties dictionary (using pprint)
|
||||||
pprint.pprint(self.props)
|
pprint.pprint(self.props)
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Returns the properties dictionary"""
|
# Returns the properties dictionary
|
||||||
return self.props
|
return self.props
|
||||||
|
|
||||||
def update(self, key, val):
|
def update(self, key, val):
|
||||||
"""Updates property in the properties dictionary [ update("pvp", "true") ] and returns boolean condition"""
|
# Updates property in the properties dictionary [ update("pvp", "true") ]
|
||||||
|
# and returns boolean condition
|
||||||
if key in self.props.keys():
|
if key in self.props.keys():
|
||||||
self.props[key] = val
|
self.props[key] = val
|
||||||
return True
|
return True
|
||||||
@ -46,10 +47,10 @@ class ServerProps:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Writes to the new file"""
|
# Writes to the new file
|
||||||
with open(self.filepath, "a+", encoding='utf-8') as f:
|
with open(self.filepath, "a+", encoding="utf-8") as f:
|
||||||
f.truncate(0)
|
f.truncate(0)
|
||||||
with open(".header", encoding='utf-8') as header:
|
with open(".header", encoding="utf-8") as header:
|
||||||
line = header.readline()
|
line = header.readline()
|
||||||
while line:
|
while line:
|
||||||
f.write(line)
|
f.write(line)
|
||||||
|
@ -4,45 +4,32 @@ import time
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import requests
|
||||||
|
|
||||||
from app.classes.controllers.servers_controller import Servers_Controller
|
from app.classes.controllers.servers_controller import ServersController
|
||||||
from app.classes.models.server_permissions import server_permissions
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
|
|
||||||
except ModuleNotFoundError as err:
|
|
||||||
helper.auto_installer_fix(err)
|
|
||||||
|
|
||||||
class ServerJars:
|
class ServerJars:
|
||||||
|
def __init__(self, helper):
|
||||||
def __init__(self):
|
self.helper = helper
|
||||||
self.base_url = "https://serverjars.com"
|
self.base_url = "https://serverjars.com"
|
||||||
|
|
||||||
def _get_api_result(self, call_url: str):
|
def _get_api_result(self, call_url: str):
|
||||||
full_url = f"{self.base_url}{call_url}"
|
full_url = f"{self.base_url}{call_url}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(full_url, timeout=2)
|
response = requests.get(full_url, timeout=2)
|
||||||
|
response.raise_for_status()
|
||||||
if r.status_code not in [200, 201]:
|
api_data = json.loads(response.content)
|
||||||
return {}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unable to connect to serverjar.com api due to error: {e}")
|
logger.error(f"Unable to load {full_url} api due to error: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
try:
|
api_result = api_data.get("status")
|
||||||
api_data = json.loads(r.content)
|
api_response = api_data.get("response", {})
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unable to parse serverjar.com api result due to error: {e}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
api_result = api_data.get('status')
|
|
||||||
api_response = api_data.get('response', {})
|
|
||||||
|
|
||||||
if api_result != "success":
|
if api_result != "success":
|
||||||
logger.error(f"Api returned a failed status: {api_result}")
|
logger.error(f"Api returned a failed status: {api_result}")
|
||||||
@ -50,12 +37,11 @@ class ServerJars:
|
|||||||
|
|
||||||
return api_response
|
return api_response
|
||||||
|
|
||||||
@staticmethod
|
def _read_cache(self):
|
||||||
def _read_cache():
|
cache_file = self.helper.serverjar_cache
|
||||||
cache_file = helper.serverjar_cache
|
|
||||||
cache = {}
|
cache = {}
|
||||||
try:
|
try:
|
||||||
with open(cache_file, "r", encoding='utf-8') as f:
|
with open(cache_file, "r", encoding="utf-8") as f:
|
||||||
cache = json.load(f)
|
cache = json.load(f)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -65,39 +51,16 @@ class ServerJars:
|
|||||||
|
|
||||||
def get_serverjar_data(self):
|
def get_serverjar_data(self):
|
||||||
data = self._read_cache()
|
data = self._read_cache()
|
||||||
return data.get('servers')
|
return data.get("servers")
|
||||||
|
|
||||||
def get_serverjar_data_sorted(self):
|
|
||||||
data = self.get_serverjar_data()
|
|
||||||
|
|
||||||
def str_to_int(x, counter=0):
|
|
||||||
try:
|
|
||||||
return ord(x[0]) + str_to_int(x[1:], counter + 1) + len(x)
|
|
||||||
except IndexError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def to_int(x):
|
|
||||||
try:
|
|
||||||
return int(x)
|
|
||||||
except ValueError:
|
|
||||||
temp = x.split('-')
|
|
||||||
return to_int(temp[0]) + str_to_int(temp[1]) / 100000
|
|
||||||
|
|
||||||
sort_key_fn = lambda x: [to_int(y) for y in x.split('.')]
|
|
||||||
|
|
||||||
for key in data.keys():
|
|
||||||
data[key] = sorted(data[key], key=sort_key_fn)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _check_api_alive(self):
|
def _check_api_alive(self):
|
||||||
logger.info("Checking serverjars.com API status")
|
logger.info("Checking serverjars.com API status")
|
||||||
|
|
||||||
check_url = f"{self.base_url}/api/fetchTypes"
|
check_url = f"{self.base_url}/api/fetchTypes"
|
||||||
try:
|
try:
|
||||||
r = requests.get(check_url, timeout=2)
|
response = requests.get(check_url, timeout=2)
|
||||||
|
|
||||||
if r.status_code in [200, 201]:
|
if response.status_code in [200, 201]:
|
||||||
logger.info("Serverjars.com API is alive")
|
logger.info("Serverjars.com API is alive")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -109,8 +72,8 @@ class ServerJars:
|
|||||||
|
|
||||||
def refresh_cache(self):
|
def refresh_cache(self):
|
||||||
|
|
||||||
cache_file = helper.serverjar_cache
|
cache_file = self.helper.serverjar_cache
|
||||||
cache_old = helper.is_file_older_than_x_days(cache_file)
|
cache_old = self.helper.is_file_older_than_x_days(cache_file)
|
||||||
|
|
||||||
# debug override
|
# debug override
|
||||||
# cache_old = True
|
# cache_old = True
|
||||||
@ -125,10 +88,7 @@ class ServerJars:
|
|||||||
if cache_old:
|
if cache_old:
|
||||||
logger.info("Cache file is over 1 day old, refreshing")
|
logger.info("Cache file is over 1 day old, refreshing")
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
data = {
|
data = {"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), "servers": {}}
|
||||||
'last_refreshed': now.strftime("%m/%d/%Y, %H:%M:%S"),
|
|
||||||
'servers': {}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar_types = self._get_server_type_list()
|
jar_types = self._get_server_type_list()
|
||||||
|
|
||||||
@ -140,81 +100,89 @@ class ServerJars:
|
|||||||
# jar versions for this server
|
# jar versions for this server
|
||||||
versions = self._get_jar_details(s)
|
versions = self._get_jar_details(s)
|
||||||
|
|
||||||
# add these versions (a list) to the dict with a key of the server type
|
# add these versions (a list) to the dict with
|
||||||
data['servers'].update({
|
# a key of the server type
|
||||||
s: versions
|
data["servers"].update({s: versions})
|
||||||
})
|
|
||||||
|
|
||||||
# save our cache
|
# save our cache
|
||||||
try:
|
try:
|
||||||
with open(cache_file, "w", encoding='utf-8') as f:
|
with open(cache_file, "w", encoding="utf-8") as f:
|
||||||
f.write(json.dumps(data, indent=4))
|
f.write(json.dumps(data, indent=4))
|
||||||
logger.info("Cache file refreshed")
|
logger.info("Cache file refreshed")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unable to update serverjars.com cache file: {e}")
|
logger.error(f"Unable to update serverjars.com cache file: {e}")
|
||||||
|
|
||||||
def _get_jar_details(self, jar_type='servers'):
|
def _get_jar_details(self, jar_type="servers"):
|
||||||
url = f'/api/fetchAll/{jar_type}'
|
url = f"/api/fetchAll/{jar_type}"
|
||||||
response = self._get_api_result(url)
|
response = self._get_api_result(url)
|
||||||
temp = []
|
temp = []
|
||||||
for v in response:
|
for v in response:
|
||||||
temp.append(v.get('version'))
|
temp.append(v.get("version"))
|
||||||
time.sleep(.5)
|
time.sleep(0.5)
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
def _get_server_type_list(self):
|
def _get_server_type_list(self):
|
||||||
url = '/api/fetchTypes/'
|
url = "/api/fetchTypes/"
|
||||||
response = self._get_api_result(url)
|
response = self._get_api_result(url)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def download_jar(self, server, version, path, server_id):
|
def download_jar(self, server, version, path, server_id):
|
||||||
update_thread = threading.Thread(target=self.a_download_jar, daemon=True, args=(server, version, path, server_id))
|
update_thread = threading.Thread(
|
||||||
|
name=f"server_download-{server_id}-{server}-{version}",
|
||||||
|
target=self.a_download_jar,
|
||||||
|
daemon=True,
|
||||||
|
args=(server, version, path, server_id),
|
||||||
|
)
|
||||||
update_thread.start()
|
update_thread.start()
|
||||||
|
|
||||||
def a_download_jar(self, server, version, path, server_id):
|
def a_download_jar(self, server, version, path, server_id):
|
||||||
#delaying download for server register to finish
|
# delaying download for server register to finish
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}"
|
fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}"
|
||||||
server_users = server_permissions.get_server_user_list(server_id)
|
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||||
|
|
||||||
|
# We need to make sure the server is registered before
|
||||||
#We need to make sure the server is registered before we submit a db update for it's stats.
|
# we submit a db update for it's stats.
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
Servers_Controller.set_download(server_id)
|
ServersController.set_download(server_id)
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
self.helper.websocket_helper.broadcast_user(
|
||||||
})
|
user, "send_start_reload", {}
|
||||||
|
)
|
||||||
|
|
||||||
break
|
break
|
||||||
except:
|
except Exception as ex:
|
||||||
logger.debug("server not registered yet. Delaying download.")
|
logger.debug(f"server not registered yet. Delaying download - {ex}")
|
||||||
|
|
||||||
# open a file stream
|
# open a file stream
|
||||||
with requests.get(fetch_url, timeout=2, stream=True) as r:
|
with requests.get(fetch_url, timeout=2, stream=True) as r:
|
||||||
try:
|
try:
|
||||||
with open(path, 'wb') as output:
|
with open(path, "wb") as output:
|
||||||
shutil.copyfileobj(r.raw, output)
|
shutil.copyfileobj(r.raw, output)
|
||||||
Servers_Controller.finish_download(server_id)
|
ServersController.finish_download(server_id)
|
||||||
|
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
websocket_helper.broadcast_user(user, 'notification', "Executable download finished")
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
user, "notification", "Executable download finished"
|
||||||
|
)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
self.helper.websocket_helper.broadcast_user(
|
||||||
})
|
user, "send_start_reload", {}
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unable to save jar to {path} due to error:{e}")
|
logger.error(f"Unable to save jar to {path} due to error:{e}")
|
||||||
Servers_Controller.finish_download(server_id)
|
ServersController.finish_download(server_id)
|
||||||
server_users = server_permissions.get_server_user_list(server_id)
|
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
websocket_helper.broadcast_user(user, 'notification', "Executable download finished")
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
user, "notification", "Executable download finished"
|
||||||
|
)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
self.helper.websocket_helper.broadcast_user(
|
||||||
})
|
user, "send_start_reload", {}
|
||||||
|
)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
server_jar_obj = ServerJars()
|
|
||||||
|
@ -5,15 +5,16 @@ import base64
|
|||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from app.classes.minecraft.mc_ping import ping
|
from app.classes.minecraft.mc_ping import ping
|
||||||
from app.classes.models.management import Host_Stats
|
from app.classes.models.management import HostStats
|
||||||
from app.classes.models.servers import servers_helper
|
from app.classes.models.servers import HelperServers
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.helpers import Helpers
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Stats:
|
|
||||||
|
|
||||||
def __init__(self, controller):
|
class Stats:
|
||||||
|
def __init__(self, helper, controller):
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
def get_node_stats(self):
|
def get_node_stats(self):
|
||||||
@ -24,30 +25,26 @@ class Stats:
|
|||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
cpu_freq = psutil._common.scpufreq(current=0, min=0, max=0)
|
cpu_freq = psutil._common.scpufreq(current=0, min=0, max=0)
|
||||||
node_stats = {
|
node_stats = {
|
||||||
'boot_time': str(boot_time),
|
"boot_time": str(boot_time),
|
||||||
'cpu_usage': psutil.cpu_percent(interval=0.5) / psutil.cpu_count(),
|
"cpu_usage": psutil.cpu_percent(interval=0.5) / psutil.cpu_count(),
|
||||||
'cpu_count': psutil.cpu_count(),
|
"cpu_count": psutil.cpu_count(),
|
||||||
'cpu_cur_freq': round(cpu_freq[0], 2),
|
"cpu_cur_freq": round(cpu_freq[0], 2),
|
||||||
'cpu_max_freq': cpu_freq[2],
|
"cpu_max_freq": cpu_freq[2],
|
||||||
'mem_percent': psutil.virtual_memory()[2],
|
"mem_percent": psutil.virtual_memory()[2],
|
||||||
'mem_usage': helper.human_readable_file_size(psutil.virtual_memory()[3]),
|
"mem_usage": Helpers.human_readable_file_size(psutil.virtual_memory()[3]),
|
||||||
'mem_total': helper.human_readable_file_size(psutil.virtual_memory()[0]),
|
"mem_total": Helpers.human_readable_file_size(psutil.virtual_memory()[0]),
|
||||||
'disk_data': self._all_disk_usage()
|
"disk_data": self._all_disk_usage(),
|
||||||
}
|
}
|
||||||
#server_stats = self.get_servers_stats()
|
# server_stats = self.get_servers_stats()
|
||||||
#data['servers'] = server_stats
|
# data['servers'] = server_stats
|
||||||
data['node_stats'] = node_stats
|
data["node_stats"] = node_stats
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_process_stats(process):
|
def _get_process_stats(process):
|
||||||
if process is None:
|
if process is None:
|
||||||
process_stats = {
|
process_stats = {"cpu_usage": 0, "memory_usage": 0, "mem_percentage": 0}
|
||||||
'cpu_usage': 0,
|
|
||||||
'memory_usage': 0,
|
|
||||||
'mem_percentage': 0
|
|
||||||
}
|
|
||||||
return process_stats
|
return process_stats
|
||||||
else:
|
else:
|
||||||
process_pid = process.pid
|
process_pid = process.pid
|
||||||
@ -63,31 +60,35 @@ class Stats:
|
|||||||
# this is a faster way of getting data for a process
|
# this is a faster way of getting data for a process
|
||||||
with p.oneshot():
|
with p.oneshot():
|
||||||
process_stats = {
|
process_stats = {
|
||||||
'cpu_usage': real_cpu,
|
"cpu_usage": real_cpu,
|
||||||
'memory_usage': helper.human_readable_file_size(p.memory_info()[0]),
|
"memory_usage": Helpers.human_readable_file_size(
|
||||||
'mem_percentage': round(p.memory_percent(), 0)
|
p.memory_info()[0]
|
||||||
|
),
|
||||||
|
"mem_percentage": round(p.memory_percent(), 0),
|
||||||
}
|
}
|
||||||
return process_stats
|
return process_stats
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unable to get process details for pid: {process_pid} due to error: {e}")
|
logger.error(
|
||||||
|
f"Unable to get process details for pid: {process_pid} Error: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
# Dummy Data
|
# Dummy Data
|
||||||
process_stats = {
|
process_stats = {
|
||||||
'cpu_usage': 0,
|
"cpu_usage": 0,
|
||||||
'memory_usage': 0,
|
"memory_usage": 0,
|
||||||
}
|
}
|
||||||
return process_stats
|
return process_stats
|
||||||
|
|
||||||
# shamelessly stolen from https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py
|
# Source: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _all_disk_usage():
|
def _all_disk_usage():
|
||||||
disk_data = []
|
disk_data = []
|
||||||
# print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type","Mount"))
|
# print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type","Mount"))
|
||||||
|
|
||||||
for part in psutil.disk_partitions(all=False):
|
for part in psutil.disk_partitions(all=False):
|
||||||
if helper.is_os_windows():
|
if Helpers.is_os_windows():
|
||||||
if 'cdrom' in part.opts or part.fstype == '':
|
if "cdrom" in part.opts or part.fstype == "":
|
||||||
# skip cd-rom drives with no disk in it; they may raise
|
# skip cd-rom drives with no disk in it; they may raise
|
||||||
# ENOENT, pop-up a Windows GUI error for a non-ready
|
# ENOENT, pop-up a Windows GUI error for a non-ready
|
||||||
# partition or just hang.
|
# partition or just hang.
|
||||||
@ -95,13 +96,13 @@ class Stats:
|
|||||||
usage = psutil.disk_usage(part.mountpoint)
|
usage = psutil.disk_usage(part.mountpoint)
|
||||||
disk_data.append(
|
disk_data.append(
|
||||||
{
|
{
|
||||||
'device': part.device,
|
"device": part.device,
|
||||||
'total': helper.human_readable_file_size(usage.total),
|
"total": Helpers.human_readable_file_size(usage.total),
|
||||||
'used': helper.human_readable_file_size(usage.used),
|
"used": Helpers.human_readable_file_size(usage.used),
|
||||||
'free': helper.human_readable_file_size(usage.free),
|
"free": Helpers.human_readable_file_size(usage.free),
|
||||||
'percent_used': int(usage.percent),
|
"percent_used": int(usage.percent),
|
||||||
'fs': part.fstype,
|
"fs": part.fstype,
|
||||||
'mount': part.mountpoint
|
"mount": part.mountpoint,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -112,15 +113,15 @@ class Stats:
|
|||||||
|
|
||||||
total_size = 0
|
total_size = 0
|
||||||
|
|
||||||
total_size = helper.get_dir_size(server_path)
|
total_size = Helpers.get_dir_size(server_path)
|
||||||
|
|
||||||
level_total_size = helper.human_readable_file_size(total_size)
|
level_total_size = Helpers.human_readable_file_size(total_size)
|
||||||
|
|
||||||
return level_total_size
|
return level_total_size
|
||||||
|
|
||||||
def get_server_players(self, server_id):
|
def get_server_players(self, server_id):
|
||||||
|
|
||||||
server = servers_helper.get_server_data_by_id(server_id)
|
server = HelperServers.get_server_data_by_id(server_id)
|
||||||
|
|
||||||
logger.info(f"Getting players for server {server}")
|
logger.info(f"Getting players for server {server}")
|
||||||
|
|
||||||
@ -128,22 +129,20 @@ class Stats:
|
|||||||
# server_settings = server.get('server_settings', {})
|
# server_settings = server.get('server_settings', {})
|
||||||
# server_data = server.get('server_data_obj', {})
|
# server_data = server.get('server_data_obj', {})
|
||||||
|
|
||||||
|
|
||||||
# TODO: search server properties file for possible override of 127.0.0.1
|
# TODO: search server properties file for possible override of 127.0.0.1
|
||||||
internal_ip = server['server_ip']
|
internal_ip = server["server_ip"]
|
||||||
server_port = server['server_port']
|
server_port = server["server_port"]
|
||||||
|
|
||||||
logger.debug("Pinging {internal_ip} on port {server_port}")
|
logger.debug(f"Pinging {internal_ip} on port {server_port}")
|
||||||
if servers_helper.get_server_type_by_id(server_id) != 'minecraft-bedrock':
|
if HelperServers.get_server_type_by_id(server_id) != "minecraft-bedrock":
|
||||||
int_mc_ping = ping(internal_ip, int(server_port))
|
int_mc_ping = ping(internal_ip, int(server_port))
|
||||||
|
|
||||||
|
|
||||||
ping_data = {}
|
ping_data = {}
|
||||||
|
|
||||||
# if we got a good ping return, let's parse it
|
# if we got a good ping return, let's parse it
|
||||||
if int_mc_ping:
|
if int_mc_ping:
|
||||||
ping_data = Stats.parse_server_ping(int_mc_ping)
|
ping_data = Stats.parse_server_ping(int_mc_ping)
|
||||||
return ping_data['players']
|
return ping_data["players"]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -156,87 +155,64 @@ class Stats:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(f"Unable to read json from ping_obj: {e}")
|
logger.info(f"Unable to read json from ping_obj: {e}")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_icon = base64.encodebytes(ping_obj.icon)
|
server_icon = base64.encodebytes(ping_obj.icon)
|
||||||
server_icon = server_icon.decode('utf-8')
|
server_icon = server_icon.decode("utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
server_icon = False
|
server_icon = False
|
||||||
logger.info(f"Unable to read the server icon : {e}")
|
logger.info(f"Unable to read the server icon : {e}")
|
||||||
|
|
||||||
ping_data = {
|
ping_data = {
|
||||||
'online': online_stats.get("online", 0),
|
"online": online_stats.get("online", 0),
|
||||||
'max': online_stats.get('max', 0),
|
"max": online_stats.get("max", 0),
|
||||||
'players': online_stats.get('players', 0),
|
"players": online_stats.get("players", 0),
|
||||||
'server_description': ping_obj.description,
|
"server_description": ping_obj.description,
|
||||||
'server_version': ping_obj.version,
|
"server_version": ping_obj.version,
|
||||||
'server_icon': server_icon
|
"server_icon": server_icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ping_data
|
return ping_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_server_RakNet_ping(ping_obj: object):
|
def parse_server_raknet_ping(ping_obj: object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_icon = base64.encodebytes(ping_obj['icon'])
|
server_icon = base64.encodebytes(ping_obj["icon"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
server_icon = False
|
server_icon = False
|
||||||
logger.info(f"Unable to read the server icon : {e}")
|
logger.info(f"Unable to read the server icon : {e}")
|
||||||
ping_data = {
|
ping_data = {
|
||||||
'online': ping_obj['server_player_count'],
|
"online": ping_obj["server_player_count"],
|
||||||
'max': ping_obj['server_player_max'],
|
"max": ping_obj["server_player_max"],
|
||||||
'players': [],
|
"players": [],
|
||||||
'server_description': ping_obj['server_edition'],
|
"server_description": ping_obj["server_edition"],
|
||||||
'server_version': ping_obj['server_version_name'],
|
"server_version": ping_obj["server_version_name"],
|
||||||
'server_icon': server_icon
|
"server_icon": server_icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return ping_data
|
return ping_data
|
||||||
|
|
||||||
|
|
||||||
def record_stats(self):
|
def record_stats(self):
|
||||||
stats_to_send = self.get_node_stats()
|
stats_to_send = self.get_node_stats()
|
||||||
node_stats = stats_to_send.get('node_stats')
|
node_stats = stats_to_send.get("node_stats")
|
||||||
|
|
||||||
Host_Stats.insert({
|
HostStats.insert(
|
||||||
Host_Stats.boot_time: node_stats.get('boot_time', "Unknown"),
|
{
|
||||||
Host_Stats.cpu_usage: round(node_stats.get('cpu_usage', 0), 2),
|
HostStats.boot_time: node_stats.get("boot_time", "Unknown"),
|
||||||
Host_Stats.cpu_cores: node_stats.get('cpu_count', 0),
|
HostStats.cpu_usage: round(node_stats.get("cpu_usage", 0), 2),
|
||||||
Host_Stats.cpu_cur_freq: node_stats.get('cpu_cur_freq', 0),
|
HostStats.cpu_cores: node_stats.get("cpu_count", 0),
|
||||||
Host_Stats.cpu_max_freq: node_stats.get('cpu_max_freq', 0),
|
HostStats.cpu_cur_freq: node_stats.get("cpu_cur_freq", 0),
|
||||||
Host_Stats.mem_usage: node_stats.get('mem_usage', "0 MB"),
|
HostStats.cpu_max_freq: node_stats.get("cpu_max_freq", 0),
|
||||||
Host_Stats.mem_percent: node_stats.get('mem_percent', 0),
|
HostStats.mem_usage: node_stats.get("mem_usage", "0 MB"),
|
||||||
Host_Stats.mem_total: node_stats.get('mem_total', "0 MB"),
|
HostStats.mem_percent: node_stats.get("mem_percent", 0),
|
||||||
Host_Stats.disk_json: node_stats.get('disk_data', '{}')
|
HostStats.mem_total: node_stats.get("mem_total", "0 MB"),
|
||||||
}).execute()
|
HostStats.disk_json: node_stats.get("disk_data", "{}"),
|
||||||
|
}
|
||||||
# server_stats = stats_to_send.get('servers')#
|
).execute()
|
||||||
#
|
|
||||||
# for server in server_stats:
|
|
||||||
# Server_Stats.insert({
|
|
||||||
# Server_Stats.server_id: server.get('id', 0),
|
|
||||||
# Server_Stats.started: server.get('started', ""),
|
|
||||||
# Server_Stats.running: server.get('running', False),
|
|
||||||
# Server_Stats.cpu: server.get('cpu', 0),
|
|
||||||
# Server_Stats.mem: server.get('mem', 0),
|
|
||||||
# Server_Stats.mem_percent: server.get('mem_percent', 0),
|
|
||||||
# Server_Stats.world_name: server.get('world_name', ""),
|
|
||||||
# Server_Stats.world_size: server.get('world_size', ""),
|
|
||||||
# Server_Stats.server_port: server.get('server_port', ""),
|
|
||||||
# Server_Stats.int_ping_results: server.get('int_ping_results', False),
|
|
||||||
# Server_Stats.online: server.get("online", False),
|
|
||||||
# Server_Stats.max: server.get("max", False),
|
|
||||||
# Server_Stats.players: server.get("players", False),
|
|
||||||
# Server_Stats.desc: server.get("desc", False),
|
|
||||||
# Server_Stats.version: server.get("version", False)
|
|
||||||
# }).execute()
|
|
||||||
|
|
||||||
# delete old data
|
# delete old data
|
||||||
max_age = helper.get_setting("history_max_age")
|
max_age = self.helper.get_setting("history_max_age")
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
last_week = now.day - max_age
|
last_week = now.day - max_age
|
||||||
|
|
||||||
Host_Stats.delete().where(Host_Stats.time < last_week).execute()
|
HostStats.delete().where(HostStats.time < last_week).execute()
|
||||||
# Server_Stats.delete().where(Server_Stats.created < last_week).execute()
|
|
||||||
|
8
app/classes/models/base_model.py
Normal file
8
app/classes/models/base_model.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import peewee
|
||||||
|
|
||||||
|
database_proxy = peewee.DatabaseProxy()
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModel(peewee.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database_proxy
|
@ -1,29 +1,24 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import typing
|
||||||
|
from enum import Enum
|
||||||
|
from peewee import (
|
||||||
|
ForeignKeyField,
|
||||||
|
CharField,
|
||||||
|
IntegerField,
|
||||||
|
DoesNotExist,
|
||||||
|
)
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.models.base_model import BaseModel
|
||||||
from app.classes.shared.permission_helper import permission_helper
|
from app.classes.models.users import Users, ApiKeys, HelperUsers
|
||||||
from app.classes.models.users import Users, ApiKeys
|
from app.classes.shared.permission_helper import PermissionHelper
|
||||||
|
|
||||||
try:
|
|
||||||
from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DoesNotExist
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
#************************************************************************************************
|
|
||||||
# User_Crafty Class
|
# User_Crafty Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class User_Crafty(Model):
|
class UserCrafty(BaseModel):
|
||||||
user_id = ForeignKeyField(Users, backref='users_crafty')
|
user_id = ForeignKeyField(Users, backref="users_crafty")
|
||||||
permissions = CharField(default="00000000")
|
permissions = CharField(default="00000000")
|
||||||
limit_server_creation = IntegerField(default=-1)
|
limit_server_creation = IntegerField(default=-1)
|
||||||
limit_user_creation = IntegerField(default=0)
|
limit_user_creation = IntegerField(default=0)
|
||||||
@ -33,167 +28,212 @@ class User_Crafty(Model):
|
|||||||
created_role = IntegerField(default=0)
|
created_role = IntegerField(default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'user_crafty'
|
table_name = "user_crafty"
|
||||||
database = database
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
|
# **********************************************************************************
|
||||||
# Crafty Permissions Class
|
# Crafty Permissions Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Enum_Permissions_Crafty(Enum):
|
class EnumPermissionsCrafty(Enum):
|
||||||
Server_Creation = 0
|
SERVER_CREATION = 0
|
||||||
User_Config = 1
|
USER_CONFIG = 1
|
||||||
Roles_Config = 2
|
ROLES_CONFIG = 2
|
||||||
|
|
||||||
class Permissions_Crafty:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class PermissionsCrafty:
|
||||||
|
# **********************************************************************************
|
||||||
# Crafty Permissions Methods
|
# Crafty Permissions Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions_list():
|
def get_permissions_list():
|
||||||
permissions_list = []
|
permissions_list: typing.List[EnumPermissionsCrafty] = []
|
||||||
for member in Enum_Permissions_Crafty.__members__.items():
|
for member in EnumPermissionsCrafty.__members__.items():
|
||||||
permissions_list.append(member[1])
|
permissions_list.append(member[1])
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions(permissions_mask):
|
def get_permissions(permissions_mask):
|
||||||
permissions_list = []
|
permissions_list: typing.List[EnumPermissionsCrafty] = []
|
||||||
for member in Enum_Permissions_Crafty.__members__.items():
|
for member in EnumPermissionsCrafty.__members__.items():
|
||||||
if crafty_permissions.has_permission(permissions_mask, member[1]):
|
if PermissionsCrafty.has_permission(permissions_mask, member[1]):
|
||||||
permissions_list.append(member[1])
|
permissions_list.append(member[1])
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def has_permission(permission_mask, permission_tested: Enum_Permissions_Crafty):
|
def has_permission(
|
||||||
|
permission_mask: typing.Mapping[int, str],
|
||||||
|
permission_tested: EnumPermissionsCrafty,
|
||||||
|
):
|
||||||
result = False
|
result = False
|
||||||
if permission_mask[permission_tested.value] == '1':
|
if permission_mask[permission_tested.value] == "1":
|
||||||
result = True
|
result = True
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value):
|
def set_permission(
|
||||||
l = list(permission_mask)
|
permission_mask, permission_tested: EnumPermissionsCrafty, value
|
||||||
l[permission_tested.value] = str(value)
|
):
|
||||||
permission_mask = ''.join(l)
|
lst = list(permission_mask)
|
||||||
|
lst[permission_tested.value] = str(value)
|
||||||
|
permission_mask = "".join(lst)
|
||||||
return permission_mask
|
return permission_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permission(permission_mask, permission_tested: Enum_Permissions_Crafty):
|
def get_permission(permission_mask, permission_tested: EnumPermissionsCrafty):
|
||||||
return permission_mask[permission_tested.value]
|
return permission_mask[permission_tested.value]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_crafty_permissions_mask(user_id):
|
def get_crafty_permissions_mask(user_id):
|
||||||
permissions_mask = ''
|
permissions_mask = ""
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
permissions_mask = user_crafty.permissions
|
permissions_mask = user_crafty.permissions
|
||||||
return permissions_mask
|
return permissions_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_permission_quantity_list():
|
def get_all_permission_quantity_list():
|
||||||
quantity_list = {
|
quantity_list = {
|
||||||
Enum_Permissions_Crafty.Server_Creation.name: -1,
|
EnumPermissionsCrafty.SERVER_CREATION.name: -1,
|
||||||
Enum_Permissions_Crafty.User_Config.name: -1,
|
EnumPermissionsCrafty.USER_CONFIG.name: -1,
|
||||||
Enum_Permissions_Crafty.Roles_Config.name: -1,
|
EnumPermissionsCrafty.ROLES_CONFIG.name: -1,
|
||||||
}
|
}
|
||||||
return quantity_list
|
return quantity_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permission_quantity_list(user_id):
|
def get_permission_quantity_list(user_id):
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
quantity_list = {
|
quantity_list = {
|
||||||
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation,
|
EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.limit_server_creation, # pylint: disable=line-too-long
|
||||||
Enum_Permissions_Crafty.User_Config.name: user_crafty.limit_user_creation,
|
EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.limit_user_creation,
|
||||||
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.limit_role_creation,
|
EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.limit_role_creation,
|
||||||
}
|
}
|
||||||
return quantity_list
|
return quantity_list
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# User_Crafty Methods
|
# User_Crafty Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_User_Crafty(user_id):
|
def get_user_crafty(user_id):
|
||||||
try:
|
try:
|
||||||
user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get()
|
user_crafty = UserCrafty.select().where(UserCrafty.user_id == user_id).get()
|
||||||
except DoesNotExist:
|
except DoesNotExist:
|
||||||
user_crafty = User_Crafty.insert({
|
user_crafty = UserCrafty.insert(
|
||||||
User_Crafty.user_id: user_id,
|
{
|
||||||
User_Crafty.permissions: "000",
|
UserCrafty.user_id: user_id,
|
||||||
User_Crafty.limit_server_creation: 0,
|
UserCrafty.permissions: "000",
|
||||||
User_Crafty.limit_user_creation: 0,
|
UserCrafty.limit_server_creation: 0,
|
||||||
User_Crafty.limit_role_creation: 0,
|
UserCrafty.limit_user_creation: 0,
|
||||||
User_Crafty.created_server: 0,
|
UserCrafty.limit_role_creation: 0,
|
||||||
User_Crafty.created_user: 0,
|
UserCrafty.created_server: 0,
|
||||||
User_Crafty.created_role: 0,
|
UserCrafty.created_user: 0,
|
||||||
}).execute()
|
UserCrafty.created_role: 0,
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
}
|
||||||
|
).execute()
|
||||||
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
return user_crafty
|
return user_crafty
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_user_crafty(user_id, uc_permissions):
|
def add_user_crafty(user_id, uc_permissions):
|
||||||
user_crafty = User_Crafty.insert({User_Crafty.user_id: user_id, User_Crafty.permissions: uc_permissions}).execute()
|
user_crafty = UserCrafty.insert(
|
||||||
|
{UserCrafty.user_id: user_id, UserCrafty.permissions: uc_permissions}
|
||||||
|
).execute()
|
||||||
return user_crafty
|
return user_crafty
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_or_update_user(user_id, permissions_mask, limit_server_creation, limit_user_creation, limit_role_creation):
|
def add_or_update_user(
|
||||||
|
user_id,
|
||||||
|
permissions_mask,
|
||||||
|
limit_server_creation,
|
||||||
|
limit_user_creation,
|
||||||
|
limit_role_creation,
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get()
|
user_crafty = UserCrafty.select().where(UserCrafty.user_id == user_id).get()
|
||||||
user_crafty.permissions = permissions_mask
|
user_crafty.permissions = permissions_mask
|
||||||
user_crafty.limit_server_creation = limit_server_creation
|
user_crafty.limit_server_creation = limit_server_creation
|
||||||
user_crafty.limit_user_creation = limit_user_creation
|
user_crafty.limit_user_creation = limit_user_creation
|
||||||
user_crafty.limit_role_creation = limit_role_creation
|
user_crafty.limit_role_creation = limit_role_creation
|
||||||
User_Crafty.save(user_crafty)
|
UserCrafty.save(user_crafty)
|
||||||
except:
|
except:
|
||||||
User_Crafty.insert({
|
UserCrafty.insert(
|
||||||
User_Crafty.user_id: user_id,
|
{
|
||||||
User_Crafty.permissions: permissions_mask,
|
UserCrafty.user_id: user_id,
|
||||||
User_Crafty.limit_server_creation: limit_server_creation,
|
UserCrafty.permissions: permissions_mask,
|
||||||
User_Crafty.limit_user_creation: limit_user_creation,
|
UserCrafty.limit_server_creation: limit_server_creation,
|
||||||
User_Crafty.limit_role_creation: limit_role_creation
|
UserCrafty.limit_user_creation: limit_user_creation,
|
||||||
}).execute()
|
UserCrafty.limit_role_creation: limit_role_creation,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_created_quantity_list(user_id):
|
def get_created_quantity_list(user_id):
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
quantity_list = {
|
quantity_list = {
|
||||||
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.created_server,
|
EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.created_server,
|
||||||
Enum_Permissions_Crafty.User_Config.name: user_crafty.created_user,
|
EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.created_user,
|
||||||
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.created_role,
|
EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.created_role,
|
||||||
}
|
}
|
||||||
return quantity_list
|
return quantity_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_crafty_limit_value(user_id, permission):
|
def get_crafty_limit_value(user_id, permission):
|
||||||
quantity_list = crafty_permissions.get_permission_quantity_list(user_id)
|
quantity_list = PermissionsCrafty.get_permission_quantity_list(user_id)
|
||||||
return quantity_list[permission]
|
return quantity_list[permission]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_add_in_crafty(user_id, permission):
|
def can_add_in_crafty(user_id, permission):
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
can = crafty_permissions.has_permission(user_crafty.permissions, permission)
|
can = PermissionsCrafty.has_permission(user_crafty.permissions, permission)
|
||||||
limit_list = crafty_permissions.get_permission_quantity_list(user_id)
|
limit_list = PermissionsCrafty.get_permission_quantity_list(user_id)
|
||||||
quantity_list = crafty_permissions.get_created_quantity_list(user_id)
|
quantity_list = PermissionsCrafty.get_created_quantity_list(user_id)
|
||||||
return can and ((quantity_list[permission.name] < limit_list[permission.name]) or limit_list[permission.name] == -1 )
|
return can and (
|
||||||
|
(quantity_list[permission.name] < limit_list[permission.name])
|
||||||
|
or limit_list[permission.name] == -1
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_server_creation(user_id):
|
def add_server_creation(user_id):
|
||||||
user_crafty = crafty_permissions.get_User_Crafty(user_id)
|
"""Increase the "Server Creation" counter for this user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (int): The modifiable user's ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The new count of servers created by this user
|
||||||
|
"""
|
||||||
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
user_crafty.created_server += 1
|
user_crafty.created_server += 1
|
||||||
User_Crafty.save(user_crafty)
|
UserCrafty.save(user_crafty)
|
||||||
return user_crafty.created_server
|
return user_crafty.created_server
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_user_creation(user_id):
|
||||||
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
|
user_crafty.created_user += 1
|
||||||
|
UserCrafty.save(user_crafty)
|
||||||
|
return user_crafty.created_user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_role_creation(user_id):
|
||||||
|
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||||
|
user_crafty.created_role += 1
|
||||||
|
UserCrafty.save(user_crafty)
|
||||||
|
return user_crafty.created_role
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_permissions_list(key: ApiKeys):
|
def get_api_key_permissions_list(key: ApiKeys):
|
||||||
user = key.user
|
user = HelperUsers.get_user(key.user_id)
|
||||||
if user.superuser and key.superuser:
|
if user["superuser"] and key.superuser:
|
||||||
return crafty_permissions.get_permissions_list()
|
return PermissionsCrafty.get_permissions_list()
|
||||||
else:
|
else:
|
||||||
user_permissions_mask = crafty_permissions.get_crafty_permissions_mask(user.user_id)
|
if user["superuser"]:
|
||||||
|
user_permissions_mask = "111"
|
||||||
|
else:
|
||||||
|
user_permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(
|
||||||
|
user["user_id"]
|
||||||
|
)
|
||||||
key_permissions_mask: str = key.crafty_permissions
|
key_permissions_mask: str = key.crafty_permissions
|
||||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
permissions_mask = PermissionHelper.combine_masks(
|
||||||
permissions_list = crafty_permissions.get_permissions(permissions_mask)
|
user_permissions_mask, key_permissions_mask
|
||||||
|
)
|
||||||
|
permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
crafty_permissions = Permissions_Crafty()
|
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
from peewee import (
|
||||||
|
ForeignKeyField,
|
||||||
|
CharField,
|
||||||
|
IntegerField,
|
||||||
|
DateTimeField,
|
||||||
|
FloatField,
|
||||||
|
TextField,
|
||||||
|
AutoField,
|
||||||
|
BooleanField,
|
||||||
|
)
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from app.classes.models.users import Users, users_helper
|
from app.classes.models.base_model import BaseModel
|
||||||
|
from app.classes.models.users import Users, HelperUsers
|
||||||
from app.classes.models.servers import Servers
|
from app.classes.models.servers import Servers
|
||||||
from app.classes.models.server_permissions import server_permissions
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
from app.classes.shared.main_models import db_helper
|
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
|
|
||||||
try:
|
|
||||||
from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DateTimeField, FloatField, TextField, AutoField, BooleanField
|
|
||||||
from playhouse.shortcuts import model_to_dict
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Class
|
# Audit_Log Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Audit_Log(Model):
|
class AuditLog(BaseModel):
|
||||||
audit_id = AutoField()
|
audit_id = AutoField()
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
user_name = CharField(default="")
|
user_name = CharField(default="")
|
||||||
user_id = IntegerField(default=0, index=True)
|
user_id = IntegerField(default=0, index=True)
|
||||||
source_ip = CharField(default='127.0.0.1')
|
source_ip = CharField(default="127.0.0.1")
|
||||||
server_id = IntegerField(default=None, index=True) # When auditing global events, use server ID 0
|
server_id = IntegerField(
|
||||||
log_msg = TextField(default='')
|
default=None, index=True
|
||||||
|
) # When auditing global events, use server ID 0
|
||||||
|
log_msg = TextField(default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
database = database
|
table_name = "audit_log"
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Host_Stats Class
|
# Host_Stats Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Host_Stats(Model):
|
class HostStats(BaseModel):
|
||||||
time = DateTimeField(default=datetime.datetime.now, index=True)
|
time = DateTimeField(default=datetime.datetime.now, index=True)
|
||||||
boot_time = CharField(default="")
|
boot_time = CharField(default="")
|
||||||
cpu_usage = FloatField(default=0)
|
cpu_usage = FloatField(default=0)
|
||||||
@ -55,30 +55,28 @@ class Host_Stats(Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "host_stats"
|
table_name = "host_stats"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Commands Class
|
# Commands Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Commands(Model):
|
class Commands(BaseModel):
|
||||||
command_id = AutoField()
|
command_id = AutoField()
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
server_id = ForeignKeyField(Servers, backref='server', index=True)
|
server_id = ForeignKeyField(Servers, backref="server", index=True)
|
||||||
user = ForeignKeyField(Users, backref='user', index=True)
|
user = ForeignKeyField(Users, backref="user", index=True)
|
||||||
source_ip = CharField(default='127.0.0.1')
|
source_ip = CharField(default="127.0.0.1")
|
||||||
command = CharField(default='')
|
command = CharField(default="")
|
||||||
executed = BooleanField(default=False)
|
executed = BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "commands"
|
table_name = "commands"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Webhooks Class
|
# Webhooks Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Webhooks(Model):
|
class Webhooks(BaseModel):
|
||||||
id = AutoField()
|
id = AutoField()
|
||||||
name = CharField(max_length=64, unique=True, index=True)
|
name = CharField(max_length=64, unique=True, index=True)
|
||||||
method = CharField(default="POST")
|
method = CharField(default="POST")
|
||||||
@ -88,15 +86,14 @@ class Webhooks(Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "webhooks"
|
table_name = "webhooks"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Schedules Class
|
# Schedules Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Schedules(Model):
|
class Schedules(BaseModel):
|
||||||
schedule_id = IntegerField(unique=True, primary_key=True)
|
schedule_id = IntegerField(unique=True, primary_key=True)
|
||||||
server_id = ForeignKeyField(Servers, backref='schedule_server')
|
server_id = ForeignKeyField(Servers, backref="schedule_server")
|
||||||
enabled = BooleanField()
|
enabled = BooleanField()
|
||||||
action = CharField()
|
action = CharField()
|
||||||
interval = IntegerField()
|
interval = IntegerField()
|
||||||
@ -110,44 +107,49 @@ class Schedules(Model):
|
|||||||
delay = IntegerField(default=0)
|
delay = IntegerField(default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'schedules'
|
table_name = "schedules"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Backups Class
|
# Backups Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Backups(Model):
|
class Backups(BaseModel):
|
||||||
excluded_dirs = CharField(null=True)
|
excluded_dirs = CharField(null=True)
|
||||||
max_backups = IntegerField()
|
max_backups = IntegerField()
|
||||||
server_id = ForeignKeyField(Servers, backref='backups_server')
|
server_id = ForeignKeyField(Servers, backref="backups_server")
|
||||||
compress = BooleanField(default=False)
|
compress = BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'backups'
|
table_name = "backups"
|
||||||
database = database
|
|
||||||
|
|
||||||
class helpers_management:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class HelpersManagement:
|
||||||
|
def __init__(self, database, helper):
|
||||||
|
self.database = database
|
||||||
|
self.helper = helper
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# Host_Stats Methods
|
# Host_Stats Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_latest_hosts_stats():
|
def get_latest_hosts_stats():
|
||||||
#pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
query = Host_Stats.select().order_by(Host_Stats.id.desc()).get()
|
query = HostStats.select().order_by(HostStats.id.desc()).get()
|
||||||
return model_to_dict(query)
|
return model_to_dict(query)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Commands Methods
|
# Commands Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_command(server_id, user_id, remote_ip, command):
|
def add_command(server_id, user_id, remote_ip, command):
|
||||||
Commands.insert({
|
Commands.insert(
|
||||||
Commands.server_id: server_id,
|
{
|
||||||
Commands.user: user_id,
|
Commands.server_id: server_id,
|
||||||
Commands.source_ip: remote_ip,
|
Commands.user: user_id,
|
||||||
Commands.command: command
|
Commands.source_ip: remote_ip,
|
||||||
}).execute()
|
Commands.command: command,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_unactioned_commands():
|
def get_unactioned_commands():
|
||||||
@ -158,72 +160,80 @@ class helpers_management:
|
|||||||
def mark_command_complete(command_id=None):
|
def mark_command_complete(command_id=None):
|
||||||
if command_id is not None:
|
if command_id is not None:
|
||||||
logger.debug(f"Marking Command {command_id} completed")
|
logger.debug(f"Marking Command {command_id} completed")
|
||||||
Commands.update({
|
Commands.update({Commands.executed: True}).where(
|
||||||
Commands.executed: True
|
Commands.command_id == command_id
|
||||||
}).where(Commands.command_id == command_id).execute()
|
).execute()
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# Audit_Log Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_actity_log():
|
def get_actity_log():
|
||||||
q = Audit_Log.select()
|
query = AuditLog.select()
|
||||||
return db_helper.return_db_rows(q)
|
return DatabaseShortcuts.return_db_rows(query)
|
||||||
|
|
||||||
@staticmethod
|
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
|
||||||
def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None):
|
|
||||||
logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ")
|
logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ")
|
||||||
user_data = users_helper.get_user(user_id)
|
user_data = HelperUsers.get_user(user_id)
|
||||||
|
|
||||||
audit_msg = f"{str(user_data['username']).capitalize()} {log_msg}"
|
audit_msg = f"{str(user_data['username']).capitalize()} {log_msg}"
|
||||||
|
|
||||||
server_users = server_permissions.get_server_user_list(server_id)
|
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
websocket_helper.broadcast_user(user,'notification', audit_msg)
|
try:
|
||||||
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
user, "notification", audit_msg
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error broadcasting to user {user} - {e}")
|
||||||
|
|
||||||
Audit_Log.insert({
|
AuditLog.insert(
|
||||||
Audit_Log.user_name: user_data['username'],
|
{
|
||||||
Audit_Log.user_id: user_id,
|
AuditLog.user_name: user_data["username"],
|
||||||
Audit_Log.server_id: server_id,
|
AuditLog.user_id: user_id,
|
||||||
Audit_Log.log_msg: audit_msg,
|
AuditLog.server_id: server_id,
|
||||||
Audit_Log.source_ip: source_ip
|
AuditLog.log_msg: audit_msg,
|
||||||
}).execute()
|
AuditLog.source_ip: source_ip,
|
||||||
#deletes records when they're more than 100
|
}
|
||||||
ordered = Audit_Log.select().order_by(+Audit_Log.created)
|
).execute()
|
||||||
|
# deletes records when there's more than 300
|
||||||
|
ordered = AuditLog.select().order_by(+AuditLog.created)
|
||||||
for item in ordered:
|
for item in ordered:
|
||||||
if not helper.get_setting('max_audit_entries'):
|
if not self.helper.get_setting("max_audit_entries"):
|
||||||
max_entries = 300
|
max_entries = 300
|
||||||
else:
|
else:
|
||||||
max_entries = helper.get_setting('max_audit_entries')
|
max_entries = self.helper.get_setting("max_audit_entries")
|
||||||
if Audit_Log.select().count() > max_entries:
|
if AuditLog.select().count() > max_entries:
|
||||||
Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute()
|
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
@staticmethod
|
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
||||||
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
|
AuditLog.insert(
|
||||||
Audit_Log.insert({
|
{
|
||||||
Audit_Log.user_name: user_name,
|
AuditLog.user_name: user_name,
|
||||||
Audit_Log.user_id: user_id,
|
AuditLog.user_id: user_id,
|
||||||
Audit_Log.server_id: server_id,
|
AuditLog.server_id: server_id,
|
||||||
Audit_Log.log_msg: log_msg,
|
AuditLog.log_msg: log_msg,
|
||||||
Audit_Log.source_ip: source_ip
|
AuditLog.source_ip: source_ip,
|
||||||
}).execute()
|
}
|
||||||
#deletes records when they're more than 100
|
).execute()
|
||||||
ordered = Audit_Log.select().order_by(+Audit_Log.created)
|
# deletes records when there's more than 300
|
||||||
|
ordered = AuditLog.select().order_by(+AuditLog.created)
|
||||||
for item in ordered:
|
for item in ordered:
|
||||||
#configurable through app/config/config.json
|
# configurable through app/config/config.json
|
||||||
if not helper.get_setting('max_audit_entries'):
|
if not self.helper.get_setting("max_audit_entries"):
|
||||||
max_entries = 300
|
max_entries = 300
|
||||||
else:
|
else:
|
||||||
max_entries = helper.get_setting('max_audit_entries')
|
max_entries = self.helper.get_setting("max_audit_entries")
|
||||||
if Audit_Log.select().count() > max_entries:
|
if AuditLog.select().count() > max_entries:
|
||||||
Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute()
|
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
#************************************************************************************************
|
|
||||||
|
# **********************************************************************************
|
||||||
# Schedules Methods
|
# Schedules Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_scheduled_task(
|
def create_scheduled_task(
|
||||||
server_id,
|
server_id,
|
||||||
@ -235,24 +245,26 @@ class helpers_management:
|
|||||||
comment=None,
|
comment=None,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
one_time=False,
|
one_time=False,
|
||||||
cron_string='* * * * *',
|
cron_string="* * * * *",
|
||||||
parent=None,
|
parent=None,
|
||||||
delay=0):
|
delay=0,
|
||||||
sch_id = Schedules.insert({
|
):
|
||||||
Schedules.server_id: server_id,
|
sch_id = Schedules.insert(
|
||||||
Schedules.action: action,
|
{
|
||||||
Schedules.enabled: enabled,
|
Schedules.server_id: server_id,
|
||||||
Schedules.interval: interval,
|
Schedules.action: action,
|
||||||
Schedules.interval_type: interval_type,
|
Schedules.enabled: enabled,
|
||||||
Schedules.start_time: start_time,
|
Schedules.interval: interval,
|
||||||
Schedules.command: command,
|
Schedules.interval_type: interval_type,
|
||||||
Schedules.comment: comment,
|
Schedules.start_time: start_time,
|
||||||
Schedules.one_time: one_time,
|
Schedules.command: command,
|
||||||
Schedules.cron_string: cron_string,
|
Schedules.comment: comment,
|
||||||
Schedules.parent: parent,
|
Schedules.one_time: one_time,
|
||||||
Schedules.delay: delay
|
Schedules.cron_string: cron_string,
|
||||||
|
Schedules.parent: parent,
|
||||||
}).execute()
|
Schedules.delay: delay,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
return sch_id
|
return sch_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -282,7 +294,11 @@ class helpers_management:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_child_schedules_by_server(schedule_id, server_id):
|
def get_child_schedules_by_server(schedule_id, server_id):
|
||||||
return Schedules.select().where(Schedules.server_id == server_id, Schedules.parent == schedule_id).execute()
|
return (
|
||||||
|
Schedules.select()
|
||||||
|
.where(Schedules.server_id == server_id, Schedules.parent == schedule_id)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_child_schedules(schedule_id):
|
def get_child_schedules(schedule_id):
|
||||||
@ -294,22 +310,27 @@ class helpers_management:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_schedules_enabled():
|
def get_schedules_enabled():
|
||||||
#pylint: disable=singleton-comparison
|
return (
|
||||||
return Schedules.select().where(Schedules.enabled == True).execute()
|
Schedules.select()
|
||||||
|
.where(Schedules.enabled == True) # pylint: disable=singleton-comparison
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Backups Methods
|
# Backups Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_backup_config(server_id):
|
def get_backup_config(server_id):
|
||||||
try:
|
try:
|
||||||
row = Backups.select().where(Backups.server_id == server_id).join(Servers)[0]
|
row = (
|
||||||
|
Backups.select().where(Backups.server_id == server_id).join(Servers)[0]
|
||||||
|
)
|
||||||
conf = {
|
conf = {
|
||||||
"backup_path": row.server_id.backup_path,
|
"backup_path": row.server_id.backup_path,
|
||||||
"excluded_dirs": row.excluded_dirs,
|
"excluded_dirs": row.excluded_dirs,
|
||||||
"max_backups": row.max_backups,
|
"max_backups": row.max_backups,
|
||||||
"server_id": row.server_id.server_id,
|
"server_id": row.server_id.server_id,
|
||||||
"compress": row.compress
|
"compress": row.compress,
|
||||||
}
|
}
|
||||||
except IndexError:
|
except IndexError:
|
||||||
conf = {
|
conf = {
|
||||||
@ -321,8 +342,14 @@ class helpers_management:
|
|||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
@staticmethod
|
def set_backup_config(
|
||||||
def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, excluded_dirs: list = None, compress: bool = False):
|
self,
|
||||||
|
server_id: int,
|
||||||
|
backup_path: str = None,
|
||||||
|
max_backups: int = None,
|
||||||
|
excluded_dirs: list = None,
|
||||||
|
compress: bool = False,
|
||||||
|
):
|
||||||
logger.debug(f"Updating server {server_id} backup config with {locals()}")
|
logger.debug(f"Updating server {server_id} backup config with {locals()}")
|
||||||
if Backups.select().where(Backups.server_id == server_id).count() != 0:
|
if Backups.select().where(Backups.server_id == server_id).count() != 0:
|
||||||
new_row = False
|
new_row = False
|
||||||
@ -331,34 +358,46 @@ class helpers_management:
|
|||||||
conf = {
|
conf = {
|
||||||
"excluded_dirs": None,
|
"excluded_dirs": None,
|
||||||
"max_backups": 0,
|
"max_backups": 0,
|
||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"compress": False
|
"compress": False,
|
||||||
}
|
}
|
||||||
new_row = True
|
new_row = True
|
||||||
if max_backups is not None:
|
if max_backups is not None:
|
||||||
conf['max_backups'] = max_backups
|
conf["max_backups"] = max_backups
|
||||||
if excluded_dirs is not None:
|
if excluded_dirs is not None:
|
||||||
dirs_to_exclude = ",".join(excluded_dirs)
|
dirs_to_exclude = ",".join(excluded_dirs)
|
||||||
conf['excluded_dirs'] = dirs_to_exclude
|
conf["excluded_dirs"] = dirs_to_exclude
|
||||||
conf['compress'] = compress
|
conf["compress"] = compress
|
||||||
if not new_row:
|
if not new_row:
|
||||||
with database.atomic():
|
with self.database.atomic():
|
||||||
if backup_path is not None:
|
if backup_path is not None:
|
||||||
u1 = Servers.update(backup_path=backup_path).where(Servers.server_id == server_id).execute()
|
server_rows = (
|
||||||
|
Servers.update(backup_path=backup_path)
|
||||||
|
.where(Servers.server_id == server_id)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
u1 = 0
|
server_rows = 0
|
||||||
u2 = Backups.update(conf).where(Backups.server_id == server_id).execute()
|
backup_rows = (
|
||||||
logger.debug(f"Updating existing backup record. {u1}+{u2} rows affected")
|
Backups.update(conf).where(Backups.server_id == server_id).execute()
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
f"Updating existing backup record. "
|
||||||
|
f"{server_rows}+{backup_rows} rows affected"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
with database.atomic():
|
with self.database.atomic():
|
||||||
conf["server_id"] = server_id
|
conf["server_id"] = server_id
|
||||||
if backup_path is not None:
|
if backup_path is not None:
|
||||||
Servers.update(backup_path=backup_path).where(Servers.server_id == server_id)
|
Servers.update(backup_path=backup_path).where(
|
||||||
|
Servers.server_id == server_id
|
||||||
|
)
|
||||||
Backups.create(**conf)
|
Backups.create(**conf)
|
||||||
logger.debug("Creating new backup record.")
|
logger.debug("Creating new backup record.")
|
||||||
|
|
||||||
def get_excluded_backup_dirs(self, server_id: int):
|
@staticmethod
|
||||||
excluded_dirs = self.get_backup_config(server_id)['excluded_dirs']
|
def get_excluded_backup_dirs(server_id: int):
|
||||||
|
excluded_dirs = HelpersManagement.get_backup_config(server_id)["excluded_dirs"]
|
||||||
if excluded_dirs is not None and excluded_dirs != "":
|
if excluded_dirs is not None and excluded_dirs != "":
|
||||||
dir_list = excluded_dirs.split(",")
|
dir_list = excluded_dirs.split(",")
|
||||||
else:
|
else:
|
||||||
@ -366,29 +405,31 @@ class helpers_management:
|
|||||||
return dir_list
|
return dir_list
|
||||||
|
|
||||||
def add_excluded_backup_dir(self, server_id: int, dir_to_add: str):
|
def add_excluded_backup_dir(self, server_id: int, dir_to_add: str):
|
||||||
dir_list = self.get_excluded_backup_dirs()
|
dir_list = self.get_excluded_backup_dirs(server_id)
|
||||||
if dir_to_add not in dir_list:
|
if dir_to_add not in dir_list:
|
||||||
dir_list.append(dir_to_add)
|
dir_list.append(dir_to_add)
|
||||||
excluded_dirs = ",".join(dir_list)
|
excluded_dirs = ",".join(dir_list)
|
||||||
self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs)
|
self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Not adding {dir_to_add} to excluded directories - already in the excluded directory list for server ID {server_id}")
|
logger.debug(
|
||||||
|
f"Not adding {dir_to_add} to excluded directories - "
|
||||||
|
f"already in the excluded directory list for server ID {server_id}"
|
||||||
|
)
|
||||||
|
|
||||||
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
|
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
|
||||||
dir_list = self.get_excluded_backup_dirs()
|
dir_list = self.get_excluded_backup_dirs(server_id)
|
||||||
if dir_to_del in dir_list:
|
if dir_to_del in dir_list:
|
||||||
dir_list.remove(dir_to_del)
|
dir_list.remove(dir_to_del)
|
||||||
excluded_dirs = ",".join(dir_list)
|
excluded_dirs = ",".join(dir_list)
|
||||||
self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs)
|
self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Not removing {dir_to_del} from excluded directories - not in the excluded directory list for server ID {server_id}")
|
logger.debug(
|
||||||
|
f"Not removing {dir_to_del} from excluded directories - "
|
||||||
|
f"not in the excluded directory list for server ID {server_id}"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def clear_unexecuted_commands():
|
def clear_unexecuted_commands():
|
||||||
Commands.update({
|
Commands.update({Commands.executed: True}).where(
|
||||||
Commands.executed: True
|
Commands.executed == False # pylint: disable=singleton-comparison
|
||||||
#pylint: disable=singleton-comparison
|
).execute()
|
||||||
}).where(Commands.executed == False).execute()
|
|
||||||
|
|
||||||
|
|
||||||
management_helper = helpers_management()
|
|
||||||
|
@ -1,26 +1,23 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
import typing as t
|
||||||
|
from peewee import (
|
||||||
|
CharField,
|
||||||
|
DoesNotExist,
|
||||||
|
AutoField,
|
||||||
|
DateTimeField,
|
||||||
|
)
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.models.base_model import BaseModel
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
try:
|
|
||||||
from peewee import SqliteDatabase, Model, CharField, DoesNotExist, AutoField, DateTimeField
|
|
||||||
from playhouse.shortcuts import model_to_dict
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Roles Class
|
# Roles Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Roles(Model):
|
class Roles(BaseModel):
|
||||||
role_id = AutoField()
|
role_id = AutoField()
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
last_update = DateTimeField(default=datetime.datetime.now)
|
last_update = DateTimeField(default=datetime.datetime.now)
|
||||||
@ -28,16 +25,22 @@ class Roles(Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "roles"
|
table_name = "roles"
|
||||||
database = database
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
|
# **********************************************************************************
|
||||||
# Roles Helpers
|
# Roles Helpers
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class helper_roles:
|
class HelperRoles:
|
||||||
|
def __init__(self, database):
|
||||||
|
self.database = database
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_roles():
|
def get_all_roles():
|
||||||
query = Roles.select()
|
return Roles.select()
|
||||||
return query
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_role_ids() -> t.List[int]:
|
||||||
|
return [role.role_id for role in Roles.select(Roles.role_id).execute()]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_roleid_by_name(role_name):
|
def get_roleid_by_name(role_name):
|
||||||
@ -50,28 +53,43 @@ class helper_roles:
|
|||||||
def get_role(role_id):
|
def get_role(role_id):
|
||||||
return model_to_dict(Roles.get(Roles.role_id == role_id))
|
return model_to_dict(Roles.get(Roles.role_id == role_id))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_role_columns(
|
||||||
|
role_id: t.Union[str, int], column_names: t.List[str]
|
||||||
|
) -> t.List[t.Any]:
|
||||||
|
columns = [getattr(Roles, column) for column in column_names]
|
||||||
|
return model_to_dict(
|
||||||
|
Roles.select(*columns).where(Roles.role_id == role_id).get(),
|
||||||
|
only=columns,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_role_column(role_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||||
|
column = getattr(Roles, column_name)
|
||||||
|
return model_to_dict(
|
||||||
|
Roles.select(column).where(Roles.role_id == role_id).get(),
|
||||||
|
only=[column],
|
||||||
|
)[column_name]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_role(role_name):
|
def add_role(role_name):
|
||||||
role_id = Roles.insert({
|
role_id = Roles.insert(
|
||||||
Roles.role_name: role_name.lower(),
|
{
|
||||||
Roles.created: helper.get_time_as_string()
|
Roles.role_name: role_name.lower(),
|
||||||
}).execute()
|
Roles.created: Helpers.get_time_as_string(),
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
return role_id
|
return role_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_role(role_id, up_data):
|
def update_role(role_id, up_data):
|
||||||
return Roles.update(up_data).where(Roles.role_id == role_id).execute()
|
return Roles.update(up_data).where(Roles.role_id == role_id).execute()
|
||||||
|
|
||||||
@staticmethod
|
def remove_role(self, role_id):
|
||||||
def remove_role(role_id):
|
return Roles.delete().where(Roles.role_id == role_id).execute()
|
||||||
with database.atomic():
|
|
||||||
role = Roles.get(Roles.role_id == role_id)
|
|
||||||
return role.delete_instance()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def role_id_exists(role_id):
|
def role_id_exists(role_id) -> bool:
|
||||||
if not roles_helper.get_role(role_id):
|
if not HelperRoles.get_role(role_id):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
roles_helper = helper_roles()
|
|
||||||
|
@ -1,184 +1,242 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import typing as t
|
||||||
|
from enum import Enum
|
||||||
|
from peewee import (
|
||||||
|
ForeignKeyField,
|
||||||
|
CharField,
|
||||||
|
CompositeKey,
|
||||||
|
JOIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
from app.classes.models.base_model import BaseModel
|
||||||
from app.classes.models.servers import Servers
|
from app.classes.models.servers import Servers
|
||||||
from app.classes.models.roles import Roles
|
from app.classes.models.roles import Roles
|
||||||
from app.classes.models.users import User_Roles, users_helper, ApiKeys, Users
|
from app.classes.models.users import UserRoles, HelperUsers, ApiKeys, Users
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.permission_helper import PermissionHelper
|
||||||
from app.classes.shared.permission_helper import permission_helper
|
|
||||||
|
|
||||||
try:
|
|
||||||
from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, CompositeKey, JOIN
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Role Servers Class
|
# Role Servers Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Role_Servers(Model):
|
class RoleServers(BaseModel):
|
||||||
role_id = ForeignKeyField(Roles, backref='role_server')
|
role_id = ForeignKeyField(Roles, backref="role_server")
|
||||||
server_id = ForeignKeyField(Servers, backref='role_server')
|
server_id = ForeignKeyField(Servers, backref="role_server")
|
||||||
permissions = CharField(default="00000000")
|
permissions = CharField(default="00000000")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'role_servers'
|
table_name = "role_servers"
|
||||||
primary_key = CompositeKey('role_id', 'server_id')
|
primary_key = CompositeKey("role_id", "server_id")
|
||||||
database = database
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
|
# **********************************************************************************
|
||||||
# Servers Permissions Class
|
# Servers Permissions Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Enum_Permissions_Server(Enum):
|
class EnumPermissionsServer(Enum):
|
||||||
Commands = 0
|
COMMANDS = 0
|
||||||
Terminal = 1
|
TERMINAL = 1
|
||||||
Logs = 2
|
LOGS = 2
|
||||||
Schedule = 3
|
SCHEDULE = 3
|
||||||
Backup = 4
|
BACKUP = 4
|
||||||
Files = 5
|
FILES = 5
|
||||||
Config = 6
|
CONFIG = 6
|
||||||
Players = 7
|
PLAYERS = 7
|
||||||
|
|
||||||
class Permissions_Servers:
|
|
||||||
|
|
||||||
|
class PermissionsServers:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_or_create(role_id, server, permissions_mask):
|
def get_or_create(role_id, server, permissions_mask):
|
||||||
return Role_Servers.get_or_create(role_id=role_id, server_id=server, permissions=permissions_mask)
|
return RoleServers.get_or_create(
|
||||||
|
role_id=role_id, server_id=server, permissions=permissions_mask
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions_list():
|
def get_permissions_list():
|
||||||
permissions_list = []
|
permissions_list: t.List[EnumPermissionsServer] = []
|
||||||
for member in Enum_Permissions_Server.__members__.items():
|
for member in EnumPermissionsServer.__members__.items():
|
||||||
permissions_list.append(member[1])
|
permissions_list.append(member[1])
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions(permissions_mask):
|
def get_permissions(permissions_mask):
|
||||||
permissions_list = []
|
permissions_list: t.List[EnumPermissionsServer] = []
|
||||||
for member in Enum_Permissions_Server.__members__.items():
|
for member in EnumPermissionsServer.__members__.items():
|
||||||
if server_permissions.has_permission(permissions_mask, member[1]):
|
if PermissionsServers.has_permission(permissions_mask, member[1]):
|
||||||
permissions_list.append(member[1])
|
permissions_list.append(member[1])
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def has_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
def has_permission(permission_mask, permission_tested: EnumPermissionsServer):
|
||||||
return permission_mask[permission_tested.value] == '1'
|
return permission_mask[permission_tested.value] == "1"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value):
|
def set_permission(
|
||||||
|
permission_mask, permission_tested: EnumPermissionsServer, value
|
||||||
|
):
|
||||||
list_perms = list(permission_mask)
|
list_perms = list(permission_mask)
|
||||||
list_perms[permission_tested.value] = str(value)
|
list_perms[permission_tested.value] = str(value)
|
||||||
permission_mask = ''.join(list_perms)
|
permission_mask = "".join(list_perms)
|
||||||
return permission_mask
|
return permission_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
def get_permission(permission_mask, permission_tested: EnumPermissionsServer):
|
||||||
return permission_mask[permission_tested.value]
|
return permission_mask[permission_tested.value]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_token_permissions(permissions_mask, api_permissions_mask):
|
def get_token_permissions(permissions_mask, api_permissions_mask):
|
||||||
permissions_list = []
|
permissions_list = []
|
||||||
for member in Enum_Permissions_Server.__members__.items():
|
for member in EnumPermissionsServer.__members__.items():
|
||||||
if permission_helper.both_have_perm(permissions_mask, api_permissions_mask, member[1]):
|
if PermissionHelper.both_have_perm(
|
||||||
|
permissions_mask, api_permissions_mask, member[1]
|
||||||
|
):
|
||||||
permissions_list.append(member[1])
|
permissions_list.append(member[1])
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
#************************************************************************************************
|
|
||||||
# Role_Servers Methods
|
# Role_Servers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_role_servers_from_role_id(roleid):
|
def get_role_servers_from_role_id(roleid: t.Union[str, int]):
|
||||||
return Role_Servers.select().where(Role_Servers.role_id == roleid)
|
return RoleServers.select().where(RoleServers.role_id == roleid)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_servers_from_role(role_id):
|
def get_servers_from_role(role_id: t.Union[str, int]):
|
||||||
return Role_Servers.select().join(Servers, JOIN.INNER).where(Role_Servers.role_id == role_id)
|
return (
|
||||||
|
RoleServers.select()
|
||||||
|
.join(Servers, JOIN.INNER)
|
||||||
|
.where(RoleServers.role_id == role_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_server_ids_from_role(role_id: t.Union[str, int]) -> t.List[int]:
|
||||||
|
# FIXME: somehow retrieve only the server ids, not the whole servers
|
||||||
|
return [
|
||||||
|
role_servers.server_id.server_id
|
||||||
|
for role_servers in (
|
||||||
|
RoleServers.select(RoleServers.server_id).where(
|
||||||
|
RoleServers.role_id == role_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_roles_from_server(server_id):
|
def get_roles_from_server(server_id):
|
||||||
return Role_Servers.select().join(Roles, JOIN.INNER).where(Role_Servers.server_id == server_id)
|
return (
|
||||||
|
RoleServers.select()
|
||||||
|
.join(Roles, JOIN.INNER)
|
||||||
|
.where(RoleServers.server_id == server_id)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
||||||
servers = Role_Servers.insert({Role_Servers.server_id: server_id, Role_Servers.role_id: role_id,
|
servers = RoleServers.insert(
|
||||||
Role_Servers.permissions: rs_permissions}).execute()
|
{
|
||||||
|
RoleServers.server_id: server_id,
|
||||||
|
RoleServers.role_id: role_id,
|
||||||
|
RoleServers.permissions: rs_permissions,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
return servers
|
return servers
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_permissions_mask(role_id, server_id):
|
def get_permissions_mask(role_id, server_id):
|
||||||
permissions_mask = ''
|
permissions_mask = ""
|
||||||
role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get()
|
role_server = (
|
||||||
|
RoleServers.select()
|
||||||
|
.where(RoleServers.role_id == role_id)
|
||||||
|
.where(RoleServers.server_id == server_id)
|
||||||
|
.get()
|
||||||
|
)
|
||||||
permissions_mask = role_server.permissions
|
permissions_mask = role_server.permissions
|
||||||
return permissions_mask
|
return permissions_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_roles(server_id):
|
def get_server_roles(server_id):
|
||||||
role_list = []
|
role_list = []
|
||||||
roles = Role_Servers.select().where(Role_Servers.server_id == server_id).execute()
|
roles = RoleServers.select().where(RoleServers.server_id == server_id).execute()
|
||||||
for role in roles:
|
for role in roles:
|
||||||
role_list.append(role.role_id)
|
role_list.append(role.role_id)
|
||||||
return role_list
|
return role_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_role_permissions_list(role_id):
|
def get_role_permissions_list(role_id):
|
||||||
permissions_mask = '00000000'
|
permissions_mask = "00000000"
|
||||||
role_server = Role_Servers.get_or_none(Role_Servers.role_id == role_id)
|
role_server = RoleServers.get_or_none(RoleServers.role_id == role_id)
|
||||||
if role_server is not None:
|
if role_server is not None:
|
||||||
permissions_mask = role_server.permissions
|
permissions_mask = role_server.permissions
|
||||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
permissions_list = PermissionsServers.get_permissions(permissions_mask)
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_role_permission(role_id, server_id, permissions_mask):
|
def get_role_permissions_dict(role_id):
|
||||||
role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get()
|
permissions_dict: t.Dict[str, t.List[EnumPermissionsServer]] = {}
|
||||||
role_server.permissions = permissions_mask
|
role_servers = RoleServers.select(
|
||||||
Role_Servers.save(role_server)
|
RoleServers.server_id, RoleServers.permissions
|
||||||
|
).where(RoleServers.role_id == role_id)
|
||||||
|
for role_server in role_servers:
|
||||||
|
permissions_dict[
|
||||||
|
role_server.server_id_id
|
||||||
|
] = PermissionsServers.get_permissions(role_server.permissions)
|
||||||
|
return permissions_dict
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_roles_permissions(role_id, removed_servers=None):
|
def update_role_permission(role_id, server_id, permissions_mask):
|
||||||
if removed_servers is None:
|
role_server = (
|
||||||
removed_servers = {}
|
RoleServers.select()
|
||||||
return Role_Servers.delete().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id.in_(removed_servers)).execute()
|
.where(RoleServers.role_id == role_id)
|
||||||
|
.where(RoleServers.server_id == server_id)
|
||||||
|
.get()
|
||||||
|
)
|
||||||
|
role_server.permissions = permissions_mask
|
||||||
|
RoleServers.save(role_server)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_roles_permissions(
|
||||||
|
role_id: t.Union[str, int], removed_servers: t.Sequence[t.Union[str, int]]
|
||||||
|
):
|
||||||
|
return (
|
||||||
|
RoleServers.delete()
|
||||||
|
.where(RoleServers.role_id == role_id)
|
||||||
|
.where(RoleServers.server_id.in_(removed_servers))
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_roles_of_server(server_id):
|
def remove_roles_of_server(server_id):
|
||||||
with database.atomic():
|
return RoleServers.delete().where(RoleServers.server_id == server_id).execute()
|
||||||
return Role_Servers.delete().where(Role_Servers.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_id_permissions_mask(user_id, server_id: str):
|
def get_user_id_permissions_mask(user_id, server_id: str):
|
||||||
user = users_helper.get_user_model(user_id)
|
user = HelperUsers.get_user_model(user_id)
|
||||||
return server_permissions.get_user_permissions_mask(user, server_id)
|
return PermissionsServers.get_user_permissions_mask(user, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_permissions_mask(user: Users, server_id: str):
|
def get_user_permissions_mask(user: Users, server_id: str):
|
||||||
if user.superuser:
|
if user.superuser:
|
||||||
permissions_mask = '1' * len(server_permissions.get_permissions_list())
|
permissions_mask = "1" * len(PermissionsServers.get_permissions_list())
|
||||||
else:
|
else:
|
||||||
roles_list = users_helper.get_user_roles_id(user.user_id)
|
roles_list = HelperUsers.get_user_roles_id(user.user_id)
|
||||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute()
|
role_server = (
|
||||||
|
RoleServers.select()
|
||||||
|
.where(RoleServers.role_id.in_(roles_list))
|
||||||
|
.where(RoleServers.server_id == server_id)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
permissions_mask = role_server[0].permissions
|
permissions_mask = role_server[0].permissions
|
||||||
except IndexError:
|
except IndexError:
|
||||||
permissions_mask = '0' * len(server_permissions.get_permissions_list())
|
permissions_mask = "0" * len(PermissionsServers.get_permissions_list())
|
||||||
return permissions_mask
|
return permissions_mask
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_user_list(server_id):
|
def get_server_user_list(server_id):
|
||||||
final_users = []
|
final_users = []
|
||||||
server_roles = Role_Servers.select().where(Role_Servers.server_id == server_id)
|
server_roles = RoleServers.select().where(RoleServers.server_id == server_id)
|
||||||
# pylint: disable=singleton-comparison
|
super_users = Users.select().where(
|
||||||
super_users = Users.select().where(Users.superuser == True)
|
Users.superuser == True # pylint: disable=singleton-comparison
|
||||||
|
)
|
||||||
for role in server_roles:
|
for role in server_roles:
|
||||||
users = User_Roles.select().where(User_Roles.role_id == role.role_id)
|
users = UserRoles.select().where(UserRoles.role_id == role.role_id)
|
||||||
for user in users:
|
for user in users:
|
||||||
if user.user_id.user_id not in final_users:
|
if user.user_id.user_id not in final_users:
|
||||||
final_users.append(user.user_id.user_id)
|
final_users.append(user.user_id.user_id)
|
||||||
@ -189,36 +247,48 @@ class Permissions_Servers:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_id_permissions_list(user_id, server_id: str):
|
def get_user_id_permissions_list(user_id, server_id: str):
|
||||||
user = users_helper.get_user_model(user_id)
|
user = HelperUsers.get_user_model(user_id)
|
||||||
return server_permissions.get_user_permissions_list(user, server_id)
|
return PermissionsServers.get_user_permissions_list(user, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_permissions_list(user: Users, server_id: str):
|
def get_user_permissions_list(user: Users, server_id: str):
|
||||||
if user.superuser:
|
if user.superuser:
|
||||||
permissions_list = server_permissions.get_permissions_list()
|
permissions_list = PermissionsServers.get_permissions_list()
|
||||||
else:
|
else:
|
||||||
permissions_mask = server_permissions.get_user_permissions_mask(user, server_id)
|
permissions_mask = PermissionsServers.get_user_permissions_mask(
|
||||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
user, server_id
|
||||||
|
)
|
||||||
|
permissions_list = PermissionsServers.get_permissions(permissions_mask)
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_id_permissions_list(key_id, server_id: str):
|
def get_api_key_id_permissions_list(key_id, server_id: str):
|
||||||
key = ApiKeys.get(ApiKeys.token_id == key_id)
|
key = ApiKeys.get(ApiKeys.token_id == key_id)
|
||||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
return PermissionsServers.get_api_key_permissions_list(key, server_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||||
user = key.user
|
user = HelperUsers.get_user(key.user_id)
|
||||||
if user.superuser and key.superuser:
|
if user["superuser"] and key.superuser:
|
||||||
return server_permissions.get_permissions_list()
|
return PermissionsServers.get_permissions_list()
|
||||||
else:
|
else:
|
||||||
roles_list = users_helper.get_user_roles_id(user['user_id'])
|
roles_list = HelperUsers.get_user_roles_id(user["user_id"])
|
||||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute()
|
role_server = (
|
||||||
user_permissions_mask = role_server[0].permissions
|
RoleServers.select()
|
||||||
|
.where(RoleServers.role_id.in_(roles_list))
|
||||||
|
.where(RoleServers.server_id == server_id)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
user_permissions_mask = role_server[0].permissions
|
||||||
|
except:
|
||||||
|
if user["superuser"]:
|
||||||
|
user_permissions_mask = "11111111"
|
||||||
|
else:
|
||||||
|
user_permissions_mask = "00000000"
|
||||||
key_permissions_mask = key.server_permissions
|
key_permissions_mask = key.server_permissions
|
||||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
permissions_mask = PermissionHelper.combine_masks(
|
||||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
user_permissions_mask, key_permissions_mask
|
||||||
|
)
|
||||||
|
permissions_list = PermissionsServers.get_permissions(permissions_mask)
|
||||||
return permissions_list
|
return permissions_list
|
||||||
|
|
||||||
|
|
||||||
server_permissions = Permissions_Servers()
|
|
||||||
|
355
app/classes/models/server_stats.py
Normal file
355
app/classes/models/server_stats.py
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from app.classes.models.servers import Servers, HelperServers
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
|
from app.classes.shared.migration import MigrationManager
|
||||||
|
|
||||||
|
try:
|
||||||
|
from peewee import (
|
||||||
|
SqliteDatabase,
|
||||||
|
Model,
|
||||||
|
DatabaseProxy,
|
||||||
|
ForeignKeyField,
|
||||||
|
CharField,
|
||||||
|
AutoField,
|
||||||
|
DateTimeField,
|
||||||
|
BooleanField,
|
||||||
|
IntegerField,
|
||||||
|
FloatField,
|
||||||
|
)
|
||||||
|
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
Helpers.auto_installer_fix(e)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
peewee_logger = logging.getLogger("peewee")
|
||||||
|
peewee_logger.setLevel(logging.INFO)
|
||||||
|
database_stats_proxy = DatabaseProxy()
|
||||||
|
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# Servers Stats Class
|
||||||
|
# **********************************************************************************
|
||||||
|
class ServerStats(Model):
|
||||||
|
stats_id = AutoField()
|
||||||
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
|
server_id = ForeignKeyField(Servers, backref="server", index=True)
|
||||||
|
started = CharField(default="")
|
||||||
|
running = BooleanField(default=False)
|
||||||
|
cpu = FloatField(default=0)
|
||||||
|
mem = FloatField(default=0)
|
||||||
|
mem_percent = FloatField(default=0)
|
||||||
|
world_name = CharField(default="")
|
||||||
|
world_size = CharField(default="")
|
||||||
|
server_port = IntegerField(default=25565)
|
||||||
|
int_ping_results = CharField(default="")
|
||||||
|
online = IntegerField(default=0)
|
||||||
|
max = IntegerField(default=0)
|
||||||
|
players = CharField(default="")
|
||||||
|
desc = CharField(default="Unable to Connect")
|
||||||
|
version = CharField(default="")
|
||||||
|
updating = BooleanField(default=False)
|
||||||
|
waiting_start = BooleanField(default=False)
|
||||||
|
first_run = BooleanField(default=True)
|
||||||
|
crashed = BooleanField(default=False)
|
||||||
|
downloading = BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "server_stats"
|
||||||
|
database = database_stats_proxy
|
||||||
|
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# Servers_Stats Methods
|
||||||
|
# **********************************************************************************
|
||||||
|
class HelperServerStats:
|
||||||
|
def __init__(self, database):
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init_database(server_id):
|
||||||
|
try:
|
||||||
|
server = HelperServers.get_server_data_by_id(server_id)
|
||||||
|
db_folder = os.path.join(f"{server['path']}", "db_stats")
|
||||||
|
db_file = os.path.join(
|
||||||
|
db_folder,
|
||||||
|
"crafty_server_stats.sqlite",
|
||||||
|
)
|
||||||
|
database = SqliteDatabase(
|
||||||
|
db_file, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
||||||
|
)
|
||||||
|
if not os.path.exists(db_file):
|
||||||
|
try:
|
||||||
|
os.mkdir(db_folder)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(
|
||||||
|
f"Error try to create the db_stats folder for server : {ex}"
|
||||||
|
)
|
||||||
|
helper_stats = Helpers()
|
||||||
|
helper_stats.migration_dir = os.path.join(
|
||||||
|
f"{helper_stats.migration_dir}", "stats"
|
||||||
|
)
|
||||||
|
helper_stats.db_path = db_file
|
||||||
|
database_stats_proxy.initialize(database)
|
||||||
|
migration_manager = MigrationManager(database, helper_stats)
|
||||||
|
migration_manager.up() # Automatically runs migrations
|
||||||
|
database_stats_proxy.initialize(database)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(
|
||||||
|
f"Error try to look for the db_stats files for server : {ex}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def select_database(server_id):
|
||||||
|
try:
|
||||||
|
server = HelperServers.get_server_data_by_id(server_id)
|
||||||
|
db_file = os.path.join(
|
||||||
|
f"{server['path']}",
|
||||||
|
"db_stats",
|
||||||
|
"crafty_server_stats.sqlite",
|
||||||
|
)
|
||||||
|
database = SqliteDatabase(
|
||||||
|
db_file, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
||||||
|
)
|
||||||
|
database_stats_proxy.initialize(database)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(
|
||||||
|
f"Error try to look for the db_stats files for server : {ex}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_servers_stats():
|
||||||
|
servers = HelperServers.get_all_defined_servers()
|
||||||
|
server_data = []
|
||||||
|
try:
|
||||||
|
for s in servers:
|
||||||
|
HelperServerStats.select_database(s.get("server_id"))
|
||||||
|
latest = (
|
||||||
|
ServerStats.select()
|
||||||
|
.where(ServerStats.server_id == s.get("server_id"))
|
||||||
|
.order_by(ServerStats.created.desc())
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
server_data.append(
|
||||||
|
{
|
||||||
|
"server_data": s,
|
||||||
|
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||||
|
"user_command_permission": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except IndexError as ex:
|
||||||
|
logger.error(
|
||||||
|
f"Stats collection failed with error: {ex}. Was a server just created?"
|
||||||
|
)
|
||||||
|
return server_data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def insert_server_stats(server):
|
||||||
|
server_id = server.get("id", 0)
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
|
||||||
|
if server_id == 0:
|
||||||
|
logger.warning("Stats saving failed with error: Server unknown (id = 0)")
|
||||||
|
return
|
||||||
|
|
||||||
|
ServerStats.insert(
|
||||||
|
{
|
||||||
|
ServerStats.server_id: server.get("id", 0),
|
||||||
|
ServerStats.started: server.get("started", ""),
|
||||||
|
ServerStats.running: server.get("running", False),
|
||||||
|
ServerStats.cpu: server.get("cpu", 0),
|
||||||
|
ServerStats.mem: server.get("mem", 0),
|
||||||
|
ServerStats.mem_percent: server.get("mem_percent", 0),
|
||||||
|
ServerStats.world_name: server.get("world_name", ""),
|
||||||
|
ServerStats.world_size: server.get("world_size", ""),
|
||||||
|
ServerStats.server_port: server.get("server_port", ""),
|
||||||
|
ServerStats.int_ping_results: server.get("int_ping_results", False),
|
||||||
|
ServerStats.online: server.get("online", False),
|
||||||
|
ServerStats.max: server.get("max", False),
|
||||||
|
ServerStats.players: server.get("players", False),
|
||||||
|
ServerStats.desc: server.get("desc", False),
|
||||||
|
ServerStats.version: server.get("version", False),
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_old_stats(server_id, last_week):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
ServerStats.delete().where(ServerStats.created < last_week).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_latest_server_stats(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
return (
|
||||||
|
ServerStats.select()
|
||||||
|
.where(ServerStats.server_id == server_id)
|
||||||
|
.order_by(ServerStats.created.desc())
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_server_stats_by_id(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
stats = (
|
||||||
|
ServerStats.select()
|
||||||
|
.where(ServerStats.server_id == server_id)
|
||||||
|
.order_by(ServerStats.created.desc())
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
return DatabaseShortcuts.return_rows(stats)[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def server_id_exists(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
if not HelperServers.get_server_data_by_id(server_id):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sever_crashed(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(crashed=True).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_download(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(downloading=True).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def finish_download(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(downloading=False).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_download_status(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
download_status = (
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id).get()
|
||||||
|
)
|
||||||
|
return download_status.downloading
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def server_crash_reset(server_id):
|
||||||
|
if server_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(crashed=False).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_crashed(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
svr = ServerStats.select().where(ServerStats.server_id == server_id).get()
|
||||||
|
# pylint: disable=singleton-comparison
|
||||||
|
if svr.crashed == True:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_update(server_id, value):
|
||||||
|
if server_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
try:
|
||||||
|
# Checks if server even exists
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"Database entry not found! {ex}")
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(updating=value).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_update_status(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
update_status = (
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id).get()
|
||||||
|
)
|
||||||
|
return update_status.updating
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_first_run(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
# Sets first run to false
|
||||||
|
try:
|
||||||
|
# Checks if server even exists
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"Database entry not found! {ex}")
|
||||||
|
return
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(first_run=False).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_first_run(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
first_run = ServerStats.select().where(ServerStats.server_id == server_id).get()
|
||||||
|
return first_run.first_run
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_ttl_without_player(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
last_stat = (
|
||||||
|
ServerStats.select()
|
||||||
|
.where(ServerStats.server_id == server_id)
|
||||||
|
.order_by(ServerStats.created.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
last_stat_with_player = (
|
||||||
|
ServerStats.select()
|
||||||
|
.where(ServerStats.server_id == server_id)
|
||||||
|
.where(ServerStats.online > 0)
|
||||||
|
.order_by(ServerStats.created.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
return last_stat.created - last_stat_with_player.created
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def can_stop_no_players(server_id, time_limit):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
can = False
|
||||||
|
ttl_no_players = HelperServerStats.get_ttl_without_player(server_id)
|
||||||
|
if (time_limit == -1) or (ttl_no_players > time_limit):
|
||||||
|
can = True
|
||||||
|
return can
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_waiting_start(server_id, value):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
try:
|
||||||
|
# Checks if server even exists
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"Database entry not found! {ex}")
|
||||||
|
with database_stats_proxy.atomic():
|
||||||
|
ServerStats.update(waiting_start=value).where(
|
||||||
|
ServerStats.server_id == server_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_waiting_start(server_id):
|
||||||
|
HelperServerStats.select_database(server_id)
|
||||||
|
waiting_start = (
|
||||||
|
ServerStats.select().where(ServerStats.server_id == server_id).get()
|
||||||
|
)
|
||||||
|
return waiting_start.waiting_start
|
@ -1,26 +1,24 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
import typing as t
|
||||||
|
from peewee import (
|
||||||
|
CharField,
|
||||||
|
AutoField,
|
||||||
|
DateTimeField,
|
||||||
|
BooleanField,
|
||||||
|
IntegerField,
|
||||||
|
)
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
from app.classes.shared.main_models import db_helper
|
from app.classes.models.base_model import BaseModel
|
||||||
|
|
||||||
try:
|
|
||||||
from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, IntegerField, FloatField
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Class
|
# Servers Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Servers(Model):
|
class Servers(BaseModel):
|
||||||
server_id = AutoField()
|
server_id = AutoField()
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
server_uuid = CharField(default="", index=True)
|
server_uuid = CharField(default="", index=True)
|
||||||
@ -42,50 +40,18 @@ class Servers(Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "servers"
|
table_name = "servers"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Stats Class
|
|
||||||
#************************************************************************************************
|
|
||||||
class Server_Stats(Model):
|
|
||||||
stats_id = AutoField()
|
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
|
||||||
server_id = ForeignKeyField(Servers, backref='server', index=True)
|
|
||||||
started = CharField(default="")
|
|
||||||
running = BooleanField(default=False)
|
|
||||||
cpu = FloatField(default=0)
|
|
||||||
mem = FloatField(default=0)
|
|
||||||
mem_percent = FloatField(default=0)
|
|
||||||
world_name = CharField(default="")
|
|
||||||
world_size = CharField(default="")
|
|
||||||
server_port = IntegerField(default=25565)
|
|
||||||
int_ping_results = CharField(default="")
|
|
||||||
online = IntegerField(default=0)
|
|
||||||
max = IntegerField(default=0)
|
|
||||||
players = CharField(default="")
|
|
||||||
desc = CharField(default="Unable to Connect")
|
|
||||||
version = CharField(default="")
|
|
||||||
updating = BooleanField(default=False)
|
|
||||||
waiting_start = BooleanField(default=False)
|
|
||||||
first_run = BooleanField(default=True)
|
|
||||||
crashed = BooleanField(default=False)
|
|
||||||
downloading = BooleanField(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
table_name = "server_stats"
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
# Servers Class
|
# Servers Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class helper_servers:
|
class HelperServers:
|
||||||
|
def __init__(self, database):
|
||||||
|
self.database = database
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Generic Servers Methods
|
# Generic Servers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_server(
|
def create_server(
|
||||||
name: str,
|
name: str,
|
||||||
@ -97,23 +63,48 @@ class helper_servers:
|
|||||||
server_log_file: str,
|
server_log_file: str,
|
||||||
server_stop: str,
|
server_stop: str,
|
||||||
server_type: str,
|
server_type: str,
|
||||||
server_port=25565):
|
server_port: int = 25565,
|
||||||
return Servers.insert({
|
server_host: str = "127.0.0.1",
|
||||||
Servers.server_name: name,
|
) -> int:
|
||||||
Servers.server_uuid: server_uuid,
|
"""Create a server in the database
|
||||||
Servers.path: server_dir,
|
|
||||||
Servers.executable: server_file,
|
|
||||||
Servers.execution_command: server_command,
|
|
||||||
Servers.auto_start: False,
|
|
||||||
Servers.auto_start_delay: 10,
|
|
||||||
Servers.crash_detection: False,
|
|
||||||
Servers.log_path: server_log_file,
|
|
||||||
Servers.server_port: server_port,
|
|
||||||
Servers.stop_command: server_stop,
|
|
||||||
Servers.backup_path: backup_path,
|
|
||||||
Servers.type: server_type
|
|
||||||
}).execute()
|
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the server
|
||||||
|
server_uuid: This is the UUID of the server
|
||||||
|
server_dir: The directory where the server is located
|
||||||
|
backup_path: The path to the backup folder
|
||||||
|
server_command: The command to start the server
|
||||||
|
server_file: The name of the server file
|
||||||
|
server_log_file: The path to the server log file
|
||||||
|
server_stop: This is the command to stop the server
|
||||||
|
server_type: This is the type of server you're creating.
|
||||||
|
server_port: The port the server will be monitored on, defaults to 25565
|
||||||
|
server_host: The host the server will be monitored on, defaults to 127.0.0.1
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The new server's id
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
PeeweeException: If the server already exists
|
||||||
|
"""
|
||||||
|
return Servers.insert(
|
||||||
|
{
|
||||||
|
Servers.server_name: name,
|
||||||
|
Servers.server_uuid: server_uuid,
|
||||||
|
Servers.path: server_dir,
|
||||||
|
Servers.executable: server_file,
|
||||||
|
Servers.execution_command: server_command,
|
||||||
|
Servers.auto_start: False,
|
||||||
|
Servers.auto_start_delay: 10,
|
||||||
|
Servers.crash_detection: False,
|
||||||
|
Servers.log_path: server_log_file,
|
||||||
|
Servers.server_port: server_port,
|
||||||
|
Servers.server_ip: server_host,
|
||||||
|
Servers.stop_command: server_stop,
|
||||||
|
Servers.backup_path: backup_path,
|
||||||
|
Servers.type: server_type,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_obj(server_id):
|
def get_server_obj(server_id):
|
||||||
@ -128,163 +119,53 @@ class helper_servers:
|
|||||||
def update_server(server_obj):
|
def update_server(server_obj):
|
||||||
return server_obj.save()
|
return server_obj.save()
|
||||||
|
|
||||||
@staticmethod
|
def remove_server(self, server_id):
|
||||||
def remove_server(server_id):
|
with self.database.atomic():
|
||||||
with database.atomic():
|
|
||||||
Servers.delete().where(Servers.server_id == server_id).execute()
|
Servers.delete().where(Servers.server_id == server_id).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_data_by_id(server_id):
|
def get_server_data_by_id(server_id):
|
||||||
query = Servers.select().where(Servers.server_id == server_id).limit(1)
|
query = Servers.select().where(Servers.server_id == server_id).limit(1)
|
||||||
try:
|
try:
|
||||||
return db_helper.return_rows(query)[0]
|
return DatabaseShortcuts.return_rows(query)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
#************************************************************************************************
|
@staticmethod
|
||||||
|
def get_server_columns(
|
||||||
|
server_id: t.Union[str, int], column_names: t.List[str]
|
||||||
|
) -> t.List[t.Any]:
|
||||||
|
columns = [getattr(Servers, column) for column in column_names]
|
||||||
|
return model_to_dict(
|
||||||
|
Servers.select(*columns).where(Servers.server_id == server_id).get(),
|
||||||
|
only=columns,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_server_column(server_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||||
|
column = getattr(Servers, column_name)
|
||||||
|
return model_to_dict(
|
||||||
|
Servers.select(column).where(Servers.server_id == server_id).get(),
|
||||||
|
only=[column],
|
||||||
|
)[column_name]
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# Servers Methods
|
# Servers Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_defined_servers():
|
def get_all_defined_servers():
|
||||||
query = Servers.select()
|
query = Servers.select()
|
||||||
return db_helper.return_rows(query)
|
return DatabaseShortcuts.return_rows(query)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_servers_stats():
|
def get_all_server_ids() -> t.List[int]:
|
||||||
servers = servers_helper.get_all_defined_servers()
|
return [server.server_id for server in Servers.select(Servers.server_id)]
|
||||||
server_data = []
|
|
||||||
try:
|
|
||||||
for s in servers:
|
|
||||||
latest = Server_Stats.select().where(Server_Stats.server_id == s.get('server_id')).order_by(Server_Stats.created.desc()).limit(1)
|
|
||||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0], "user_command_permission":True})
|
|
||||||
except IndexError as ex:
|
|
||||||
logger.error(f"Stats collection failed with error: {ex}. Was a server just created?")
|
|
||||||
return server_data
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_friendly_name(server_id):
|
def get_server_friendly_name(server_id):
|
||||||
server_data = servers_helper.get_server_data_by_id(server_id)
|
server_data = HelperServers.get_server_data_by_id(server_id)
|
||||||
friendly_name = f"{server_data.get('server_name', None)} with ID: {server_data.get('server_id', 0)}"
|
friendly_name = (
|
||||||
|
f"{server_data.get('server_name', None)} "
|
||||||
|
f"with ID: {server_data.get('server_id', 0)}"
|
||||||
|
)
|
||||||
return friendly_name
|
return friendly_name
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
# Servers_Stats Methods
|
|
||||||
#************************************************************************************************
|
|
||||||
@staticmethod
|
|
||||||
def get_latest_server_stats(server_id):
|
|
||||||
return Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_server_stats_by_id(server_id):
|
|
||||||
stats = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1)
|
|
||||||
return db_helper.return_rows(stats)[0]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def server_id_exists(server_id):
|
|
||||||
if not servers_helper.get_server_data_by_id(server_id):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sever_crashed(server_id):
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(crashed=True).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_download(server_id):
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(downloading=True).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def finish_download(server_id):
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(downloading=False).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_download_status(server_id):
|
|
||||||
download_status = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
|
|
||||||
return download_status.downloading
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def server_crash_reset(server_id):
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(crashed=False).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_crashed(server_id):
|
|
||||||
svr = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
|
|
||||||
#pylint: disable=singleton-comparison
|
|
||||||
if svr.crashed == True:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_update(server_id, value):
|
|
||||||
try:
|
|
||||||
#Checks if server even exists
|
|
||||||
Server_Stats.select().where(Server_Stats.server_id == server_id)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f"Database entry not found! {ex}")
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(updating=value).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_update_status(server_id):
|
|
||||||
update_status = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
|
|
||||||
return update_status.updating
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_first_run(server_id):
|
|
||||||
#Sets first run to false
|
|
||||||
try:
|
|
||||||
#Checks if server even exists
|
|
||||||
Server_Stats.select().where(Server_Stats.server_id == server_id)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f"Database entry not found! {ex}")
|
|
||||||
return
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(first_run=False).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_first_run(server_id):
|
|
||||||
first_run = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
|
|
||||||
return first_run.first_run
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_TTL_without_player(server_id):
|
|
||||||
last_stat = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).first()
|
|
||||||
last_stat_with_player = (Server_Stats
|
|
||||||
.select()
|
|
||||||
.where(Server_Stats.server_id == server_id)
|
|
||||||
.where(Server_Stats.online > 0)
|
|
||||||
.order_by(Server_Stats.created.desc())
|
|
||||||
.first())
|
|
||||||
return last_stat.created - last_stat_with_player.created
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def can_stop_no_players(server_id, time_limit):
|
|
||||||
can = False
|
|
||||||
ttl_no_players = servers_helper.get_TTL_without_player(server_id)
|
|
||||||
if (time_limit == -1) or (ttl_no_players > time_limit):
|
|
||||||
can = True
|
|
||||||
return can
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_waiting_start(server_id, value):
|
|
||||||
try:
|
|
||||||
# Checks if server even exists
|
|
||||||
Server_Stats.select().where(Server_Stats.server_id == server_id)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f"Database entry not found! {ex}")
|
|
||||||
with database.atomic():
|
|
||||||
Server_Stats.update(waiting_start=value).where(Server_Stats.server_id == server_id).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_waiting_start(server_id):
|
|
||||||
waiting_start = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
|
|
||||||
return waiting_start.waiting_start
|
|
||||||
|
|
||||||
|
|
||||||
servers_helper = helper_servers()
|
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
from typing import Optional, Union
|
import typing as t
|
||||||
|
|
||||||
from app.classes.models.roles import Roles, roles_helper
|
from peewee import (
|
||||||
from app.classes.shared.helpers import helper
|
ForeignKeyField,
|
||||||
|
CharField,
|
||||||
|
AutoField,
|
||||||
|
DateTimeField,
|
||||||
|
BooleanField,
|
||||||
|
CompositeKey,
|
||||||
|
DoesNotExist,
|
||||||
|
JOIN,
|
||||||
|
)
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
try:
|
from app.classes.shared.helpers import Helpers
|
||||||
from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, CompositeKey, DoesNotExist, JOIN
|
from app.classes.models.base_model import BaseModel
|
||||||
from playhouse.shortcuts import model_to_dict
|
from app.classes.models.roles import Roles, HelperRoles
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# Users Class
|
# Users Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class Users(Model):
|
class Users(BaseModel):
|
||||||
user_id = AutoField()
|
user_id = AutoField()
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
last_login = DateTimeField(default=datetime.datetime.now)
|
last_login = DateTimeField(default=datetime.datetime.now)
|
||||||
@ -34,48 +35,60 @@ class Users(Model):
|
|||||||
enabled = BooleanField(default=True)
|
enabled = BooleanField(default=True)
|
||||||
superuser = BooleanField(default=False)
|
superuser = BooleanField(default=False)
|
||||||
lang = CharField(default="en_EN")
|
lang = CharField(default="en_EN")
|
||||||
support_logs = CharField(default = '')
|
support_logs = CharField(default="")
|
||||||
valid_tokens_from = DateTimeField(default=datetime.datetime.now)
|
valid_tokens_from = DateTimeField(default=datetime.datetime.now)
|
||||||
server_order = CharField(default="")
|
server_order = CharField(default="")
|
||||||
|
preparing = BooleanField(default=False)
|
||||||
|
hints = BooleanField(default=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "users"
|
table_name = "users"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
# ************************************************************************************************
|
PUBLIC_USER_ATTRS: t.Final = [
|
||||||
|
"user_id",
|
||||||
|
"created",
|
||||||
|
"username",
|
||||||
|
"enabled",
|
||||||
|
"superuser",
|
||||||
|
"lang", # maybe remove?
|
||||||
|
]
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
# API Keys Class
|
# API Keys Class
|
||||||
# ************************************************************************************************
|
# **********************************************************************************
|
||||||
class ApiKeys(Model):
|
class ApiKeys(BaseModel):
|
||||||
token_id = AutoField()
|
token_id = AutoField()
|
||||||
name = CharField(default='', unique=True, index=True)
|
name = CharField(default="", unique=True, index=True)
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
user_id = ForeignKeyField(Users, backref='api_token', index=True)
|
user_id = ForeignKeyField(Users, backref="api_token", index=True)
|
||||||
server_permissions = CharField(default='00000000')
|
server_permissions = CharField(default="00000000")
|
||||||
crafty_permissions = CharField(default='000')
|
crafty_permissions = CharField(default="000")
|
||||||
superuser = BooleanField(default=False)
|
superuser = BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'api_keys'
|
table_name = "api_keys"
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# User Roles Class
|
# User Roles Class
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class User_Roles(Model):
|
class UserRoles(BaseModel):
|
||||||
user_id = ForeignKeyField(Users, backref='user_role')
|
user_id = ForeignKeyField(Users, backref="user_role")
|
||||||
role_id = ForeignKeyField(Roles, backref='user_role')
|
role_id = ForeignKeyField(Roles, backref="user_role")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'user_roles'
|
table_name = "user_roles"
|
||||||
primary_key = CompositeKey('user_id', 'role_id')
|
primary_key = CompositeKey("user_id", "role_id")
|
||||||
database = database
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
|
# **********************************************************************************
|
||||||
# Users Helpers
|
# Users Helpers
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
class helper_users:
|
class HelperUsers:
|
||||||
|
def __init__(self, database, helper):
|
||||||
|
self.database = database
|
||||||
|
self.helper = helper
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_id(user_id):
|
def get_by_id(user_id):
|
||||||
@ -86,6 +99,15 @@ class helper_users:
|
|||||||
query = Users.select().where(Users.username != "system")
|
query = Users.select().where(Users.username != "system")
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_user_ids() -> t.List[int]:
|
||||||
|
return [
|
||||||
|
user.user_id
|
||||||
|
for user in Users.select(Users.user_id)
|
||||||
|
.where(Users.username != "system")
|
||||||
|
.execute()
|
||||||
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_lang_by_id(user_id):
|
def get_user_lang_by_id(user_id):
|
||||||
return Users.get(Users.user_id == user_id).lang
|
return Users.get(Users.user_id == user_id).lang
|
||||||
@ -106,30 +128,48 @@ class helper_users:
|
|||||||
def get_user(user_id):
|
def get_user(user_id):
|
||||||
if user_id == 0:
|
if user_id == 0:
|
||||||
return {
|
return {
|
||||||
'user_id': 0,
|
"user_id": 0,
|
||||||
'created': '10/24/2019, 11:34:00',
|
"created": "10/24/2019, 11:34:00",
|
||||||
'last_login': '10/24/2019, 11:34:00',
|
"last_login": "10/24/2019, 11:34:00",
|
||||||
'last_update': '10/24/2019, 11:34:00',
|
"last_update": "10/24/2019, 11:34:00",
|
||||||
'last_ip': "127.27.23.89",
|
"last_ip": "127.27.23.89",
|
||||||
'username': "SYSTEM",
|
"username": "SYSTEM",
|
||||||
'password': None,
|
"password": None,
|
||||||
'email': "default@example.com",
|
"email": "default@example.com",
|
||||||
'enabled': True,
|
"enabled": True,
|
||||||
'superuser': True,
|
"superuser": True,
|
||||||
'roles': [],
|
"roles": [],
|
||||||
'servers': [],
|
"servers": [],
|
||||||
'support_logs': '',
|
"support_logs": "",
|
||||||
}
|
}
|
||||||
user = model_to_dict(Users.get(Users.user_id == user_id))
|
user = model_to_dict(Users.get(Users.user_id == user_id))
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
# I know it should apply it without setting it but I'm just making sure
|
# I know it should apply it without setting it but I'm just making sure
|
||||||
user = users_helper.add_user_roles(user)
|
user = HelperUsers.add_user_roles(user)
|
||||||
return user
|
return user
|
||||||
else:
|
else:
|
||||||
#logger.debug("user: ({}) {}".format(user_id, {}))
|
# logger.debug("user: ({}) {}".format(user_id, {}))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_user_columns(
|
||||||
|
user_id: t.Union[str, int], column_names: t.List[str]
|
||||||
|
) -> t.List[t.Any]:
|
||||||
|
columns = [getattr(Users, column) for column in column_names]
|
||||||
|
return model_to_dict(
|
||||||
|
Users.select(*columns).where(Users.user_id == user_id).get(),
|
||||||
|
only=columns,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_user_column(user_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||||
|
column = getattr(Users, column_name)
|
||||||
|
return model_to_dict(
|
||||||
|
Users.select(column).where(Users.user_id == user_id).get(),
|
||||||
|
only=[column],
|
||||||
|
)[column_name]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_system_user(user_id):
|
def check_system_user(user_id):
|
||||||
try:
|
try:
|
||||||
@ -142,23 +182,51 @@ class helper_users:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_model(user_id: str) -> Users:
|
def get_user_model(user_id: str) -> Users:
|
||||||
user = Users.get(Users.user_id == user_id)
|
user = Users.get(Users.user_id == user_id)
|
||||||
user = users_helper.add_user_roles(user)
|
user = HelperUsers.add_user_roles(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@staticmethod
|
def add_user(
|
||||||
def add_user(username: str, password: Optional[str] = None, email: Optional[str] = None, enabled: bool = True, superuser: bool = False) -> str:
|
self,
|
||||||
|
username: str,
|
||||||
|
password: str = None,
|
||||||
|
email: t.Optional[str] = None,
|
||||||
|
enabled: bool = True,
|
||||||
|
superuser: bool = False,
|
||||||
|
) -> str:
|
||||||
if password is not None:
|
if password is not None:
|
||||||
pw_enc = helper.encode_pass(password)
|
pw_enc = self.helper.encode_pass(password)
|
||||||
else:
|
else:
|
||||||
pw_enc = None
|
pw_enc = None
|
||||||
user_id = Users.insert({
|
user_id = Users.insert(
|
||||||
Users.username: username.lower(),
|
{
|
||||||
Users.password: pw_enc,
|
Users.username: username.lower(),
|
||||||
Users.email: email,
|
Users.password: pw_enc,
|
||||||
Users.enabled: enabled,
|
Users.email: email,
|
||||||
Users.superuser: superuser,
|
Users.enabled: enabled,
|
||||||
Users.created: helper.get_time_as_string()
|
Users.superuser: superuser,
|
||||||
}).execute()
|
Users.created: Helpers.get_time_as_string(),
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_rawpass_user(
|
||||||
|
username: str,
|
||||||
|
password: str = None,
|
||||||
|
email: t.Optional[str] = None,
|
||||||
|
enabled: bool = True,
|
||||||
|
superuser: bool = False,
|
||||||
|
) -> str:
|
||||||
|
user_id = Users.insert(
|
||||||
|
{
|
||||||
|
Users.username: username.lower(),
|
||||||
|
Users.password: password,
|
||||||
|
Users.email: email,
|
||||||
|
Users.enabled: enabled,
|
||||||
|
Users.superuser: superuser,
|
||||||
|
Users.created: Helpers.get_time_as_string(),
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -170,7 +238,9 @@ class helper_users:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_server_order(user_id, user_server_order):
|
def update_server_order(user_id, user_server_order):
|
||||||
Users.update(server_order = user_server_order).where(Users.user_id == user_id).execute()
|
Users.update(server_order=user_server_order).where(
|
||||||
|
Users.user_id == user_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_order(user_id):
|
def get_server_order(user_id):
|
||||||
@ -178,104 +248,128 @@ class helper_users:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_super_user_list():
|
def get_super_user_list():
|
||||||
final_users = []
|
final_users: t.List[int] = []
|
||||||
# pylint: disable=singleton-comparison
|
super_users = Users.select().where(
|
||||||
super_users = Users.select().where(Users.superuser == True)
|
Users.superuser == True # pylint: disable=singleton-comparison
|
||||||
|
)
|
||||||
for suser in super_users:
|
for suser in super_users:
|
||||||
if suser.user_id not in final_users:
|
if suser.user_id not in final_users:
|
||||||
final_users.append(suser.user_id)
|
final_users.append(suser.user_id)
|
||||||
return final_users
|
return final_users
|
||||||
|
|
||||||
@staticmethod
|
def remove_user(self, user_id):
|
||||||
def remove_user(user_id):
|
with self.database.atomic():
|
||||||
with database.atomic():
|
UserRoles.delete().where(UserRoles.user_id == user_id).execute()
|
||||||
User_Roles.delete().where(User_Roles.user_id == user_id).execute()
|
return Users.delete().where(Users.user_id == user_id).execute()
|
||||||
user = Users.get(Users.user_id == user_id)
|
|
||||||
return user.delete_instance()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_support_path(user_id, support_path):
|
def set_support_path(user_id, support_path):
|
||||||
Users.update(support_logs = support_path).where(Users.user_id == user_id).execute()
|
Users.update(support_logs=support_path).where(
|
||||||
|
Users.user_id == user_id
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_prepare(user_id):
|
||||||
|
Users.update(preparing=True).where(Users.user_id == user_id).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stop_prepare(user_id):
|
||||||
|
Users.update(preparing=False).where(Users.user_id == user_id).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clear_support_status():
|
||||||
|
Users.update(preparing=False).where(
|
||||||
|
Users.preparing == True # pylint: disable=singleton-comparison
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_id_exists(user_id):
|
def user_id_exists(user_id):
|
||||||
if not users_helper.get_user(user_id):
|
if not HelperUsers.get_user(user_id):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
# User_Roles Methods
|
# User_Roles Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_or_create(user_id, role_id):
|
def get_or_create(user_id, role_id):
|
||||||
return User_Roles.get_or_create(user_id=user_id, role_id=role_id)
|
return UserRoles.get_or_create(user_id=user_id, role_id=role_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_roles_id(user_id):
|
def get_user_roles_id(user_id):
|
||||||
roles_list = []
|
roles_list = []
|
||||||
roles = User_Roles.select().where(User_Roles.user_id == user_id)
|
roles = UserRoles.select().where(UserRoles.user_id == user_id)
|
||||||
for r in roles:
|
for r in roles:
|
||||||
roles_list.append(roles_helper.get_role(r.role_id)['role_id'])
|
roles_list.append(HelperRoles.get_role(r.role_id)["role_id"])
|
||||||
return roles_list
|
return roles_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_roles_names(user_id):
|
def get_user_roles_names(user_id):
|
||||||
roles_list = []
|
roles = UserRoles.select(UserRoles.role_id).where(UserRoles.user_id == user_id)
|
||||||
roles = User_Roles.select().where(User_Roles.user_id == user_id)
|
return [
|
||||||
for r in roles:
|
HelperRoles.get_role_column(role.role_id, "role_name") for role in roles
|
||||||
roles_list.append(roles_helper.get_role(r.role_id)['role_name'])
|
]
|
||||||
return roles_list
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_role_to_user(user_id, role_id):
|
def add_role_to_user(user_id, role_id):
|
||||||
User_Roles.insert({
|
UserRoles.insert(
|
||||||
User_Roles.user_id: user_id,
|
{UserRoles.user_id: user_id, UserRoles.role_id: role_id}
|
||||||
User_Roles.role_id: role_id
|
).execute()
|
||||||
}).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_user_roles(user: Union[dict, Users]):
|
def add_user_roles(user: t.Union[dict, Users]):
|
||||||
if isinstance(user, dict):
|
if isinstance(user, dict):
|
||||||
user_id = user['user_id']
|
user_id = user["user_id"]
|
||||||
else:
|
else:
|
||||||
user_id = user.user_id
|
user_id = user.user_id
|
||||||
|
|
||||||
# I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas
|
# I just copied this code from get_user,
|
||||||
|
# it had those TODOs & comments made by mac - Lukas
|
||||||
|
|
||||||
roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id)
|
roles_query = (
|
||||||
|
UserRoles.select()
|
||||||
|
.join(Roles, JOIN.INNER)
|
||||||
|
.where(UserRoles.user_id == user_id)
|
||||||
|
)
|
||||||
# TODO: this query needs to be narrower
|
# TODO: this query needs to be narrower
|
||||||
roles = set()
|
roles = set()
|
||||||
for r in roles_query:
|
for r in roles_query:
|
||||||
roles.add(r.role_id.role_id)
|
roles.add(r.role_id.role_id)
|
||||||
|
|
||||||
if isinstance(user, dict):
|
if isinstance(user, dict):
|
||||||
user['roles'] = roles
|
user["roles"] = roles
|
||||||
else:
|
else:
|
||||||
user.roles = roles
|
user.roles = roles
|
||||||
|
|
||||||
#logger.debug("user: ({}) {}".format(user_id, user))
|
# logger.debug("user: ({}) {}".format(user_id, user))
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_role_query(user_id):
|
def user_role_query(user_id):
|
||||||
user_query = User_Roles.select().where(User_Roles.user_id == user_id)
|
user_query = UserRoles.select().where(UserRoles.user_id == user_id)
|
||||||
query = Roles.select().where(Roles.role_id == -1)
|
query = Roles.select().where(Roles.role_id == -1)
|
||||||
for u in user_query:
|
for user in user_query:
|
||||||
query = query + Roles.select().where(Roles.role_id == u.role_id)
|
query = query + Roles.select().where(Roles.role_id == user.role_id)
|
||||||
return query
|
return query
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_user_roles(user_id, removed_roles):
|
def delete_user_roles(user_id, removed_roles):
|
||||||
User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute()
|
UserRoles.delete().where(UserRoles.user_id == user_id).where(
|
||||||
|
UserRoles.role_id.in_(removed_roles)
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_roles_from_role_id(role_id):
|
def remove_roles_from_role_id(role_id):
|
||||||
User_Roles.delete().where(User_Roles.role_id == role_id).execute()
|
UserRoles.delete().where(UserRoles.role_id == role_id).execute()
|
||||||
|
|
||||||
# ************************************************************************************************
|
@staticmethod
|
||||||
# ApiKeys Methods
|
def get_users_from_role(role_id):
|
||||||
# ************************************************************************************************
|
UserRoles.select().where(UserRoles.role_id == role_id).execute()
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# ApiKeys Methods
|
||||||
|
# **********************************************************************************
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_api_keys(user_id: str):
|
def get_user_api_keys(user_id: str):
|
||||||
@ -287,18 +381,29 @@ class helper_users:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_user_api_key(
|
def add_user_api_key(
|
||||||
name: str,
|
name: str,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
superuser: bool = False,
|
superuser: bool = False,
|
||||||
server_permissions_mask: Optional[str] = None,
|
server_permissions_mask: t.Optional[str] = None,
|
||||||
crafty_permissions_mask: Optional[str] = None):
|
crafty_permissions_mask: t.Optional[str] = None,
|
||||||
return ApiKeys.insert({
|
):
|
||||||
ApiKeys.name: name,
|
return ApiKeys.insert(
|
||||||
ApiKeys.user_id: user_id,
|
{
|
||||||
**({ApiKeys.server_permissions: server_permissions_mask} if server_permissions_mask is not None else {}),
|
ApiKeys.name: name,
|
||||||
**({ApiKeys.crafty_permissions: crafty_permissions_mask} if crafty_permissions_mask is not None else {}),
|
ApiKeys.user_id: user_id,
|
||||||
ApiKeys.superuser: superuser
|
**(
|
||||||
}).execute()
|
{ApiKeys.server_permissions: server_permissions_mask}
|
||||||
|
if server_permissions_mask is not None
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
**(
|
||||||
|
{ApiKeys.crafty_permissions: crafty_permissions_mask}
|
||||||
|
if crafty_permissions_mask is not None
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
ApiKeys.superuser: superuser,
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_user_api_keys(user_id: str):
|
def delete_user_api_keys(user_id: str):
|
||||||
@ -307,7 +412,3 @@ class helper_users:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_user_api_key(key_id: str):
|
def delete_user_api_key(key_id: str):
|
||||||
ApiKeys.delete().where(ApiKeys.token_id == key_id).execute()
|
ApiKeys.delete().where(ApiKeys.token_id == key_id).execute()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
users_helper = helper_users()
|
|
||||||
|
@ -1,79 +1,81 @@
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Dict, Any, Tuple
|
from typing import Optional, Dict, Any, Tuple
|
||||||
|
import jwt
|
||||||
|
from jwt import PyJWTError
|
||||||
|
|
||||||
from app.classes.models.users import users_helper, ApiKeys
|
from app.classes.models.users import HelperUsers, ApiKeys
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
|
|
||||||
try:
|
|
||||||
import jwt
|
|
||||||
from jwt import PyJWTError
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Authentication:
|
class Authentication:
|
||||||
def __init__(self):
|
def __init__(self, helper):
|
||||||
|
self.helper = helper
|
||||||
self.secret = "my secret"
|
self.secret = "my secret"
|
||||||
self.secret = helper.get_setting('apikey_secret', None)
|
self.secret = self.helper.get_setting("apikey_secret", None)
|
||||||
|
|
||||||
if self.secret is None or self.secret == 'random':
|
if self.secret is None or self.secret == "random":
|
||||||
self.secret = helper.random_string_generator(64)
|
self.secret = self.helper.random_string_generator(64)
|
||||||
|
self.helper.set_setting("apikey_secret", self.secret)
|
||||||
|
|
||||||
@staticmethod
|
def generate(self, user_id, extra=None):
|
||||||
def generate(user_id, extra=None):
|
|
||||||
if extra is None:
|
if extra is None:
|
||||||
extra = {}
|
extra = {}
|
||||||
return jwt.encode(
|
jwt_encoded = jwt.encode(
|
||||||
{
|
{"user_id": user_id, "iat": int(time.time()), **extra},
|
||||||
'user_id': user_id,
|
self.secret,
|
||||||
'iat': int(time.time()),
|
algorithm="HS256",
|
||||||
**extra
|
|
||||||
},
|
|
||||||
authentication.secret,
|
|
||||||
algorithm="HS256"
|
|
||||||
)
|
)
|
||||||
|
return jwt_encoded
|
||||||
|
|
||||||
@staticmethod
|
def read(self, token):
|
||||||
def read(token):
|
return jwt.decode(token, self.secret, algorithms=["HS256"])
|
||||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
|
||||||
|
|
||||||
@staticmethod
|
def check_no_iat(self, token) -> Optional[Dict[str, Any]]:
|
||||||
def check_no_iat(token) -> Optional[Dict[str, Any]]:
|
|
||||||
try:
|
try:
|
||||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
return jwt.decode(str(token), self.secret, algorithms=["HS256"])
|
||||||
except PyJWTError as error:
|
except PyJWTError as error:
|
||||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
def check(
|
||||||
def check(token) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
self,
|
||||||
|
token,
|
||||||
|
) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
||||||
try:
|
try:
|
||||||
data = jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
data = jwt.decode(str(token), self.secret, algorithms=["HS256"])
|
||||||
except PyJWTError as error:
|
except PyJWTError as error:
|
||||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||||
return None
|
return None
|
||||||
iat: int = data['iat']
|
iat: int = data["iat"]
|
||||||
key: Optional[ApiKeys] = None
|
key: Optional[ApiKeys] = None
|
||||||
if 'token_id' in data:
|
if "token_id" in data:
|
||||||
key_id = data['token_id']
|
key_id = data["token_id"]
|
||||||
key = users_helper.get_user_api_key(key_id)
|
key = HelperUsers.get_user_api_key(key_id)
|
||||||
if key is None:
|
if key is None:
|
||||||
return None
|
return None
|
||||||
user_id: str = data['user_id']
|
user_id: str = data["user_id"]
|
||||||
user = users_helper.get_user(user_id)
|
user = HelperUsers.get_user(user_id)
|
||||||
# TODO: Have a cache or something so we don't constantly have to query the database
|
# TODO: Have a cache or something so we don't constantly
|
||||||
if int(user.get('valid_tokens_from').timestamp()) < iat:
|
# have to query the database
|
||||||
|
if int(user.get("valid_tokens_from").timestamp()) < iat:
|
||||||
# Success!
|
# Success!
|
||||||
return key, data, user
|
return key, data, user
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
def check_err(
|
||||||
def check_bool(token) -> bool:
|
self,
|
||||||
return authentication.check(token) is not None
|
token,
|
||||||
|
) -> Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]:
|
||||||
|
# Without this function there would be runtime exceptions like the following:
|
||||||
|
# "None" object is not iterable
|
||||||
|
|
||||||
|
output = self.check(token)
|
||||||
|
if output is None:
|
||||||
|
raise Exception("Invalid token")
|
||||||
|
return output
|
||||||
|
|
||||||
authentication = Authentication()
|
def check_bool(self, token) -> bool:
|
||||||
|
return self.check(token) is not None
|
||||||
|
@ -3,75 +3,117 @@ import cmd
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
import getpass
|
||||||
|
from app.classes.shared.console import Console
|
||||||
|
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.import3 import Import3
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class MainPrompt(cmd.Cmd):
|
|
||||||
|
|
||||||
def __init__(self, tasks_manager, migration_manager):
|
class MainPrompt(cmd.Cmd):
|
||||||
|
def __init__(self, helper, tasks_manager, migration_manager, main_controller):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.helper = helper
|
||||||
self.tasks_manager = tasks_manager
|
self.tasks_manager = tasks_manager
|
||||||
self.migration_manager = migration_manager
|
self.migration_manager = migration_manager
|
||||||
|
self.controller = main_controller
|
||||||
|
|
||||||
# overrides the default Prompt
|
# overrides the default Prompt
|
||||||
prompt = f"Crafty Controller v{helper.get_version_string()} > "
|
self.prompt = f"Crafty Controller v{self.helper.get_version_string()} > "
|
||||||
|
|
||||||
|
# see MR !233 for pylint exemptino reason
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def emptyline():
|
def emptyline(): # pylint: disable=arguments-differ
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#pylint: disable=unused-argument
|
def do_exit(self, _line):
|
||||||
def do_exit(self, line):
|
|
||||||
self.tasks_manager._main_graceful_exit()
|
self.tasks_manager._main_graceful_exit()
|
||||||
self.universal_exit()
|
self.universal_exit()
|
||||||
|
|
||||||
def do_migrations(self, line):
|
def do_migrations(self, line):
|
||||||
if line == 'up':
|
if line == "up":
|
||||||
self.migration_manager.up()
|
self.migration_manager.up()
|
||||||
elif line == 'down':
|
elif line == "down":
|
||||||
self.migration_manager.down()
|
self.migration_manager.down()
|
||||||
elif line == 'done':
|
elif line == "done":
|
||||||
console.info(self.migration_manager.done)
|
Console.info(self.migration_manager.done)
|
||||||
elif line == 'todo':
|
elif line == "todo":
|
||||||
console.info(self.migration_manager.todo)
|
Console.info(self.migration_manager.todo)
|
||||||
elif line == 'diff':
|
elif line == "diff":
|
||||||
console.info(self.migration_manager.diff)
|
Console.info(self.migration_manager.diff)
|
||||||
elif line == 'info':
|
elif line == "info":
|
||||||
console.info(f'Done: {self.migration_manager.done}')
|
Console.info(f"Done: {self.migration_manager.done}")
|
||||||
console.info(f'FS: {self.migration_manager.todo}')
|
Console.info(f"FS: {self.migration_manager.todo}")
|
||||||
console.info(f'Todo: {self.migration_manager.diff}')
|
Console.info(f"Todo: {self.migration_manager.diff}")
|
||||||
elif line.startswith('add '):
|
elif line.startswith("add "):
|
||||||
migration_name = line[len('add '):]
|
migration_name = line[len("add ") :]
|
||||||
self.migration_manager.create(migration_name, False)
|
self.migration_manager.create(migration_name, False)
|
||||||
else:
|
else:
|
||||||
console.info('Unknown migration command')
|
Console.info("Unknown migration command")
|
||||||
|
|
||||||
|
def do_set_passwd(self, line):
|
||||||
|
|
||||||
|
try:
|
||||||
|
username = str(line).lower()
|
||||||
|
# If no user is found it returns None
|
||||||
|
user_id = self.controller.users.get_id_by_name(username)
|
||||||
|
if not username:
|
||||||
|
Console.error("You must enter a username. Ex: `set_passwd admin'")
|
||||||
|
return False
|
||||||
|
if not user_id:
|
||||||
|
Console.error(f"No user found by the name of {username}")
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
Console.error(f"User: {line} Not Found")
|
||||||
|
return False
|
||||||
|
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
|
||||||
|
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")
|
||||||
|
|
||||||
|
if new_pass != new_pass_conf:
|
||||||
|
Console.error("Passwords do not match. Please try again.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(new_pass) > 512:
|
||||||
|
Console.warning("Passwords must be greater than 6char long and under 512")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(new_pass) < 6:
|
||||||
|
Console.warning("Passwords must be greater than 6char long and under 512")
|
||||||
|
return False
|
||||||
|
self.controller.users.update_user(user_id, {"password": new_pass})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def do_threads(_line):
|
def do_threads(_line):
|
||||||
for thread in threading.enumerate():
|
for thread in threading.enumerate():
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
print(f'Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}')
|
print(
|
||||||
|
f"Name: {thread.name} Identifier: "
|
||||||
|
f"{thread.ident} TID/PID: {thread.native_id}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(f'Name: {thread.name} Identifier: {thread.ident}')
|
print(f"Name: {thread.name} Identifier: {thread.ident}")
|
||||||
|
|
||||||
|
def do_import3(self, _line):
|
||||||
|
Import3.start_import()
|
||||||
|
|
||||||
def universal_exit(self):
|
def universal_exit(self):
|
||||||
logger.info("Stopping all server daemons / threads")
|
logger.info("Stopping all server daemons / threads")
|
||||||
console.info("Stopping all server daemons / threads - This may take a few seconds")
|
Console.info(
|
||||||
websocket_helper.disconnect_all()
|
"Stopping all server daemons / threads - This may take a few seconds"
|
||||||
console.info('Waiting for main thread to stop')
|
)
|
||||||
|
self.helper.websocket_helper.disconnect_all()
|
||||||
|
Console.info("Waiting for main thread to stop")
|
||||||
while True:
|
while True:
|
||||||
if self.tasks_manager.get_main_thread_run_status():
|
if self.tasks_manager.get_main_thread_run_status():
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
@staticmethod
|
def help_exit(self):
|
||||||
def help_exit():
|
Console.help("Stops the server if running, Exits the program")
|
||||||
console.help("Stops the server if running, Exits the program")
|
|
||||||
|
|
||||||
@staticmethod
|
def help_migrations(self):
|
||||||
def help_migrations():
|
Console.help("Only for advanced users. Use with caution")
|
||||||
console.help("Only for advanced users. Use with caution")
|
|
||||||
|
def help_import3(self):
|
||||||
|
Console.help("Import users and servers from Crafty 3")
|
||||||
|
@ -12,61 +12,72 @@ except ModuleNotFoundError as ex:
|
|||||||
logger.critical(f"Import Error: Unable to load {ex.name} module", exc_info=True)
|
logger.critical(f"Import Error: Unable to load {ex.name} module", exc_info=True)
|
||||||
print(f"Import Error: Unable to load {ex.name} module")
|
print(f"Import Error: Unable to load {ex.name} module")
|
||||||
from app.classes.shared.installer import installer
|
from app.classes.shared.installer import installer
|
||||||
installer.do_install()
|
|
||||||
class Console:
|
|
||||||
|
|
||||||
|
installer.do_install()
|
||||||
|
|
||||||
|
|
||||||
|
class Console:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if 'colorama' in sys.modules:
|
if "colorama" in sys.modules:
|
||||||
init()
|
init()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def do_print(message, color):
|
def do_print(message, color):
|
||||||
if 'termcolor' in sys.modules or 'colorama' in sys.modules:
|
if "termcolor" in sys.modules or "colorama" in sys.modules:
|
||||||
print(colored(message, color))
|
print(colored(message, color))
|
||||||
else:
|
else:
|
||||||
print(message)
|
print(message)
|
||||||
|
|
||||||
def magenta(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "magenta")
|
def magenta(message):
|
||||||
|
Console.do_print(message, "magenta")
|
||||||
|
|
||||||
def cyan(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "cyan")
|
def cyan(message):
|
||||||
|
Console.do_print(message, "cyan")
|
||||||
|
|
||||||
def yellow(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "yellow")
|
def yellow(message):
|
||||||
|
Console.do_print(message, "yellow")
|
||||||
|
|
||||||
def red(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "red")
|
def red(message):
|
||||||
|
Console.do_print(message, "red")
|
||||||
|
|
||||||
def green(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "green")
|
def green(message):
|
||||||
|
Console.do_print(message, "green")
|
||||||
|
|
||||||
def white(self, message):
|
@staticmethod
|
||||||
self.do_print(message, "white")
|
def white(message):
|
||||||
|
Console.do_print(message, "white")
|
||||||
|
|
||||||
def debug(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def debug(message):
|
||||||
self.magenta(f"[+] Crafty: {dt} - DEBUG:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
|
||||||
|
|
||||||
def info(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def info(message):
|
||||||
self.white(f"[+] Crafty: {dt} - INFO:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.white(f"[+] Crafty: {date_time} - INFO:\t{message}")
|
||||||
|
|
||||||
def warning(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def warning(message):
|
||||||
self.cyan(f"[+] Crafty: {dt} - WARNING:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.cyan(f"[+] Crafty: {date_time} - WARNING:\t{message}")
|
||||||
|
|
||||||
def error(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def error(message):
|
||||||
self.yellow(f"[+] Crafty: {dt} - ERROR:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.yellow(f"[+] Crafty: {date_time} - ERROR:\t{message}")
|
||||||
|
|
||||||
def critical(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def critical(message):
|
||||||
self.red(f"[+] Crafty: {dt} - CRITICAL:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.red(f"[+] Crafty: {date_time} - CRITICAL:\t{message}")
|
||||||
|
|
||||||
def help(self, message):
|
@staticmethod
|
||||||
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
def help(message):
|
||||||
self.green(f"[+] Crafty: {dt} - HELP:\t{message}")
|
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||||
|
Console.green(f"[+] Crafty: {date_time} - HELP:\t{message}")
|
||||||
|
|
||||||
console = Console()
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
class CraftyException(Exception):
|
class CraftyException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DatabaseException(CraftyException):
|
class DatabaseException(CraftyException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SchemaError(DatabaseException):
|
class SchemaError(DatabaseException):
|
||||||
pass
|
pass
|
||||||
|
@ -6,19 +6,17 @@ from zipfile import ZipFile, ZIP_DEFLATED
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class FileHelpers:
|
|
||||||
allowed_quotes = [
|
|
||||||
"\"",
|
|
||||||
"'",
|
|
||||||
"`"
|
|
||||||
]
|
|
||||||
|
|
||||||
def del_dirs(self, path):
|
class FileHelpers:
|
||||||
|
allowed_quotes = ['"', "'", "`"]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def del_dirs(path):
|
||||||
path = pathlib.Path(path)
|
path = pathlib.Path(path)
|
||||||
for sub in path.iterdir():
|
for sub in path.iterdir():
|
||||||
if sub.is_dir():
|
if sub.is_dir():
|
||||||
# Delete folder if it is a folder
|
# Delete folder if it is a folder
|
||||||
self.del_dirs(sub)
|
FileHelpers.del_dirs(sub)
|
||||||
else:
|
else:
|
||||||
# Delete file if it is a file:
|
# Delete file if it is a file:
|
||||||
sub.unlink()
|
sub.unlink()
|
||||||
@ -32,7 +30,7 @@ class FileHelpers:
|
|||||||
path = pathlib.Path(path)
|
path = pathlib.Path(path)
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Deleting file: {path}")
|
logger.debug(f"Deleting file: {path}")
|
||||||
#Remove the file
|
# Remove the file
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
return True
|
return True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
@ -48,54 +46,70 @@ class FileHelpers:
|
|||||||
def copy_file(src_path, dest_path):
|
def copy_file(src_path, dest_path):
|
||||||
shutil.copy(src_path, dest_path)
|
shutil.copy(src_path, dest_path)
|
||||||
|
|
||||||
def move_dir(self, src_path, dest_path):
|
@staticmethod
|
||||||
self.copy_dir(src_path, dest_path)
|
def move_dir(src_path, dest_path):
|
||||||
self.del_dirs(src_path)
|
FileHelpers.copy_dir(src_path, dest_path)
|
||||||
|
FileHelpers.del_dirs(src_path)
|
||||||
|
|
||||||
def move_file(self, src_path, dest_path):
|
@staticmethod
|
||||||
self.copy_file(src_path, dest_path)
|
def move_file(src_path, dest_path):
|
||||||
self.del_file(src_path)
|
FileHelpers.copy_file(src_path, dest_path)
|
||||||
|
FileHelpers.del_file(src_path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_archive(path_to_destination, path_to_zip):
|
def make_archive(path_to_destination, path_to_zip):
|
||||||
# create a ZipFile object
|
# create a ZipFile object
|
||||||
path_to_destination += '.zip'
|
path_to_destination += ".zip"
|
||||||
with ZipFile(path_to_destination, 'w') as z:
|
with ZipFile(path_to_destination, "w") as zip_file:
|
||||||
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
|
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
|
||||||
ziproot = path_to_zip
|
ziproot = path_to_zip
|
||||||
for file in files:
|
for file in files:
|
||||||
try:
|
try:
|
||||||
logger.info(f"backing up: {os.path.join(root, file)}")
|
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, ""), file))
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, ""), file),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, "/"), file))
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, "/"), file),
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error backing up: {os.path.join(root, file)}! - Error was: {e}")
|
logger.warning(
|
||||||
|
f"Error backing up: {os.path.join(root, file)}!"
|
||||||
|
f" - Error was: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_compressed_archive(path_to_destination, path_to_zip):
|
def make_compressed_archive(path_to_destination, path_to_zip):
|
||||||
# create a ZipFile object
|
# create a ZipFile object
|
||||||
path_to_destination += '.zip'
|
path_to_destination += ".zip"
|
||||||
with ZipFile(path_to_destination, 'w', ZIP_DEFLATED) as z:
|
with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as zip_file:
|
||||||
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
|
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
|
||||||
ziproot = path_to_zip
|
ziproot = path_to_zip
|
||||||
for file in files:
|
for file in files:
|
||||||
try:
|
try:
|
||||||
logger.info(f"backing up: {os.path.join(root, file)}")
|
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, ""), file))
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, ""), file),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, "/"), file))
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, "/"), file),
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error backing up: {os.path.join(root, file)}! - Error was: {e}")
|
logger.warning(
|
||||||
|
f"Error backing up: {os.path.join(root, file)}!"
|
||||||
|
f" - Error was: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
file_helper = FileHelpers()
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
90
app/classes/shared/import3.py
Normal file
90
app/classes/shared/import3.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from app.classes.controllers.users_controller import HelperUsers
|
||||||
|
from app.classes.shared.console import Console
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Import3:
|
||||||
|
def __init__(self, helper, controller):
|
||||||
|
self.helper = helper
|
||||||
|
self.controller = controller
|
||||||
|
|
||||||
|
def start_import(self):
|
||||||
|
folder = os.path.normpath(
|
||||||
|
input(
|
||||||
|
"Please input the path to the migrations folder "
|
||||||
|
"in your installation of Crafty 3: "
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not os.path.exists(folder):
|
||||||
|
Console.info(
|
||||||
|
"Crafty cannot find the path you entered. "
|
||||||
|
"Does Crafty's user have permission to access it?"
|
||||||
|
)
|
||||||
|
Console.info("Please run the import3 command again and enter a valid path.")
|
||||||
|
else:
|
||||||
|
with open(os.path.join(folder, "users.json"), encoding="utf-8") as f:
|
||||||
|
user_json = json.loads(f.read())
|
||||||
|
with open(os.path.join(folder, "mc_settings.json"), encoding="utf-8") as f:
|
||||||
|
servers_json = json.loads(f.read())
|
||||||
|
self.import_users(user_json)
|
||||||
|
self.import_servers(servers_json, self.controller)
|
||||||
|
|
||||||
|
def import_users(self, json_data):
|
||||||
|
# If there is only one user to import json needs to call the data differently
|
||||||
|
if isinstance(json_data, list):
|
||||||
|
for user in json_data:
|
||||||
|
HelperUsers.add_rawpass_user(user["username"], user["password"])
|
||||||
|
Console.info(f"Imported user {user['username']} from Crafty 3")
|
||||||
|
logger.info(f"Imported user {user['username']} from Crafty 3")
|
||||||
|
else:
|
||||||
|
Console.info(
|
||||||
|
"There is only one user detected. "
|
||||||
|
"Cannot create duplicate Admin account."
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"There is only one user detected. "
|
||||||
|
"Cannot create duplicate Admin account."
|
||||||
|
)
|
||||||
|
|
||||||
|
def import_servers(self, json_data, controller):
|
||||||
|
# If there is only one server to import json needs to call the data differently
|
||||||
|
if isinstance(json_data, list):
|
||||||
|
for server in json_data:
|
||||||
|
new_server_id = controller.import_jar_server(
|
||||||
|
server_name=server["server_name"],
|
||||||
|
server_path=server["server_path"],
|
||||||
|
server_jar=server["server_jar"],
|
||||||
|
min_mem=(int(server["memory_min"]) / 1000),
|
||||||
|
max_mem=(int(server["memory_max"]) / 1000),
|
||||||
|
port=server["server_port"],
|
||||||
|
)
|
||||||
|
Console.info(
|
||||||
|
f"Imported server {server['server_name']}[{server['id']}] "
|
||||||
|
f"from Crafty 3 to new server id {new_server_id}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Imported server {server['server_name']}[{server['id']}] "
|
||||||
|
f"from Crafty 3 to new server id {new_server_id}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_server_id = controller.import_jar_server(
|
||||||
|
server_name=json_data["server_name"],
|
||||||
|
server_path=json_data["server_path"],
|
||||||
|
server_jar=json_data["server_jar"],
|
||||||
|
min_mem=(int(json_data["memory_min"]) / 1000),
|
||||||
|
max_mem=(int(json_data["memory_max"]) / 1000),
|
||||||
|
port=json_data["server_port"],
|
||||||
|
)
|
||||||
|
Console.info(
|
||||||
|
f"Imported server {json_data['server_name']}[{json_data['id']}] "
|
||||||
|
f"from Crafty 3 to new server id {new_server_id}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Imported server {json_data['server_name']}[{json_data['id']}] "
|
||||||
|
f"from Crafty 3 to new server id {new_server_id}"
|
||||||
|
)
|
@ -1,12 +1,13 @@
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
class install:
|
|
||||||
|
|
||||||
|
class Install:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_venv():
|
def is_venv():
|
||||||
return (hasattr(sys, 'real_prefix') or
|
return hasattr(sys, "real_prefix") or (
|
||||||
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))
|
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
|
||||||
|
)
|
||||||
|
|
||||||
def do_install(self):
|
def do_install(self):
|
||||||
|
|
||||||
@ -16,8 +17,11 @@ class install:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# do our pip install
|
# do our pip install
|
||||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", 'requirements.txt'])
|
subprocess.check_call(
|
||||||
|
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]
|
||||||
|
)
|
||||||
print("Crafty has installed it's dependencies, please restart Crafty")
|
print("Crafty has installed it's dependencies, please restart Crafty")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
installer = install()
|
|
||||||
|
installer = Install()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,55 +1,46 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from app.classes.models.users import Users, users_helper
|
from app.classes.shared.helpers import Helpers # pylint: disable=unused-import
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.console import console
|
|
||||||
|
|
||||||
# To disable warning about unused import ; Users is imported from here in other places
|
|
||||||
# pylint: disable=self-assigning-variable
|
|
||||||
Users = Users
|
|
||||||
|
|
||||||
try:
|
|
||||||
# pylint: disable=unused-import
|
|
||||||
from peewee import SqliteDatabase, fn
|
|
||||||
from playhouse.shortcuts import model_to_dict
|
|
||||||
|
|
||||||
except ModuleNotFoundError as err:
|
|
||||||
helper.auto_installer_fix(err)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
peewee_logger = logging.getLogger('peewee')
|
|
||||||
peewee_logger.setLevel(logging.INFO)
|
|
||||||
database = SqliteDatabase(helper.db_path, pragmas = {
|
|
||||||
'journal_mode': 'wal',
|
|
||||||
'cache_size': -1024 * 10})
|
|
||||||
|
|
||||||
class db_builder:
|
|
||||||
|
|
||||||
@staticmethod
|
class DatabaseBuilder:
|
||||||
def default_settings():
|
def __init__(self, database, helper, users_helper):
|
||||||
|
self.database = database
|
||||||
|
self.helper = helper
|
||||||
|
self.users_helper = users_helper
|
||||||
|
|
||||||
|
def default_settings(self):
|
||||||
logger.info("Fresh Install Detected - Creating Default Settings")
|
logger.info("Fresh Install Detected - Creating Default Settings")
|
||||||
console.info("Fresh Install Detected - Creating Default Settings")
|
Console.info("Fresh Install Detected - Creating Default Settings")
|
||||||
default_data = helper.find_default_password()
|
default_data = self.helper.find_default_password()
|
||||||
|
|
||||||
username = default_data.get("username", 'admin')
|
username = default_data.get("username", "admin")
|
||||||
password = default_data.get("password", 'crafty')
|
password = default_data.get("password", "crafty")
|
||||||
|
|
||||||
users_helper.add_user(username=username, password=password, email="default@example.com", superuser=True)
|
self.users_helper.add_user(
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
email="default@example.com",
|
||||||
|
superuser=True,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
def is_fresh_install(self):
|
||||||
def is_fresh_install():
|
|
||||||
try:
|
try:
|
||||||
user = users_helper.get_by_id(1)
|
user = self.users_helper.get_by_id(1)
|
||||||
if user:
|
if user:
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class db_shortcuts:
|
|
||||||
|
|
||||||
#************************************************************************************************
|
class DatabaseShortcuts:
|
||||||
|
# **********************************************************************************
|
||||||
# Generic Databse Methods
|
# Generic Databse Methods
|
||||||
#************************************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def return_rows(query):
|
def return_rows(query):
|
||||||
rows = []
|
rows = []
|
||||||
@ -67,10 +58,3 @@ class db_shortcuts:
|
|||||||
def return_db_rows(model):
|
def return_db_rows(model):
|
||||||
data = [model_to_dict(row) for row in model]
|
data = [model_to_dict(row) for row in model]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
#************************************************************************************************
|
|
||||||
# Static Accessors
|
|
||||||
#************************************************************************************************
|
|
||||||
installer = db_builder()
|
|
||||||
db_helper = db_shortcuts()
|
|
||||||
|
@ -7,24 +7,21 @@ import os
|
|||||||
import re
|
import re
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
import peewee
|
||||||
|
from playhouse.migrate import (
|
||||||
|
SqliteMigrator,
|
||||||
|
Operation,
|
||||||
|
SQL,
|
||||||
|
SqliteDatabase,
|
||||||
|
make_index_name,
|
||||||
|
)
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.helpers import Helpers
|
||||||
|
|
||||||
try:
|
|
||||||
import peewee
|
|
||||||
from playhouse.migrate import (
|
|
||||||
SqliteMigrator,
|
|
||||||
Operation, SQL, SqliteDatabase,
|
|
||||||
make_index_name
|
|
||||||
)
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
MIGRATE_TABLE = 'migratehistory'
|
MIGRATE_TABLE = "migratehistory"
|
||||||
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
||||||
import peewee
|
import peewee
|
||||||
|
|
||||||
@ -70,6 +67,7 @@ def get_model(method):
|
|||||||
if isinstance(model, str):
|
if isinstance(model, str):
|
||||||
return method(migrator, migrator.table_dict[model], *args, **kwargs)
|
return method(migrator, migrator.table_dict[model], *args, **kwargs)
|
||||||
return method(migrator, model, *args, **kwargs)
|
return method(migrator, model, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ class Migrator(object):
|
|||||||
database = database.obj
|
database = database.obj
|
||||||
self.database: SqliteDatabase = database
|
self.database: SqliteDatabase = database
|
||||||
self.table_dict: t.Dict[str, peewee.Model] = {}
|
self.table_dict: t.Dict[str, peewee.Model] = {}
|
||||||
self.operations: t.List[t.Union[Operation, callable]] = []
|
self.operations: t.List[t.Union[Operation, t.Callable]] = []
|
||||||
self.migrator = SqliteMigrator(database)
|
self.migrator = SqliteMigrator(database)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -133,11 +131,17 @@ class Migrator(object):
|
|||||||
"""
|
"""
|
||||||
for name, field in fields.items():
|
for name, field in fields.items():
|
||||||
model._meta.add_field(name, field)
|
model._meta.add_field(name, field)
|
||||||
self.operations.append(self.migrator.add_column(
|
self.operations.append(
|
||||||
model._meta.table_name, field.column_name, field))
|
self.migrator.add_column(
|
||||||
|
model._meta.table_name, field.column_name, field
|
||||||
|
)
|
||||||
|
)
|
||||||
if field.unique:
|
if field.unique:
|
||||||
self.operations.append(self.migrator.add_index(
|
self.operations.append(
|
||||||
model._meta.table_name, (field.column_name,), unique=True))
|
self.migrator.add_index(
|
||||||
|
model._meta.table_name, (field.column_name,), unique=True
|
||||||
|
)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
@ -145,19 +149,22 @@ class Migrator(object):
|
|||||||
"""
|
"""
|
||||||
Removes fields from model.
|
Removes fields from model.
|
||||||
"""
|
"""
|
||||||
fields = [field for field in model._meta.fields.values()
|
fields = [field for field in model._meta.fields.values() if field.name in names]
|
||||||
if field.name in names]
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
self.__del_field__(model, field)
|
self.__del_field__(model, field)
|
||||||
if field.unique:
|
if field.unique:
|
||||||
# Drop unique index
|
# Drop unique index
|
||||||
index_name = make_index_name(
|
index_name = make_index_name(
|
||||||
model._meta.table_name, [field.column_name])
|
model._meta.table_name, [field.column_name]
|
||||||
self.operations.append(self.migrator.drop_index(
|
)
|
||||||
model._meta.table_name, index_name))
|
self.operations.append(
|
||||||
|
self.migrator.drop_index(model._meta.table_name, index_name)
|
||||||
|
)
|
||||||
self.operations.append(
|
self.operations.append(
|
||||||
self.migrator.drop_column(
|
self.migrator.drop_column(
|
||||||
model._meta.table_name, field.column_name, cascade=False))
|
model._meta.table_name, field.column_name, cascade=False
|
||||||
|
)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def __del_field__(self, model: peewee.Model, field: peewee.Field):
|
def __del_field__(self, model: peewee.Model, field: peewee.Field):
|
||||||
@ -169,12 +176,14 @@ class Migrator(object):
|
|||||||
if isinstance(field, peewee.ForeignKeyField):
|
if isinstance(field, peewee.ForeignKeyField):
|
||||||
obj_id_name = field.column_name
|
obj_id_name = field.column_name
|
||||||
if field.column_name == field.name:
|
if field.column_name == field.name:
|
||||||
obj_id_name += '_id'
|
obj_id_name += "_id"
|
||||||
delattr(model, obj_id_name)
|
delattr(model, obj_id_name)
|
||||||
delattr(field.rel_model, field.backref)
|
delattr(field.rel_model, field.backref)
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
def rename_column(self, model: peewee.Model, old_name: str, new_name: str) -> peewee.Model:
|
def rename_column(
|
||||||
|
self, model: peewee.Model, old_name: str, new_name: str
|
||||||
|
) -> peewee.Model:
|
||||||
"""
|
"""
|
||||||
Renames field in model.
|
Renames field in model.
|
||||||
"""
|
"""
|
||||||
@ -185,9 +194,10 @@ class Migrator(object):
|
|||||||
field.name = field.column_name = new_name
|
field.name = field.column_name = new_name
|
||||||
model._meta.add_field(new_name, field)
|
model._meta.add_field(new_name, field)
|
||||||
if isinstance(field, peewee.ForeignKeyField):
|
if isinstance(field, peewee.ForeignKeyField):
|
||||||
field.column_name = new_name = field.column_name + '_id'
|
field.column_name = new_name = field.column_name + "_id"
|
||||||
self.operations.append(self.migrator.rename_column(
|
self.operations.append(
|
||||||
model._meta.table_name, old_name, new_name))
|
self.migrator.rename_column(model._meta.table_name, old_name, new_name)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
@ -203,7 +213,9 @@ class Migrator(object):
|
|||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
def add_index(self, model: peewee.Model, *columns: str, unique=False) -> peewee.Model:
|
def add_index(
|
||||||
|
self, model: peewee.Model, *columns: str, unique=False
|
||||||
|
) -> peewee.Model:
|
||||||
"""Create indexes."""
|
"""Create indexes."""
|
||||||
model._meta.indexes.append((columns, unique))
|
model._meta.indexes.append((columns, unique))
|
||||||
columns_ = []
|
columns_ = []
|
||||||
@ -215,11 +227,12 @@ class Migrator(object):
|
|||||||
field.index = not unique
|
field.index = not unique
|
||||||
|
|
||||||
if isinstance(field, peewee.ForeignKeyField):
|
if isinstance(field, peewee.ForeignKeyField):
|
||||||
col = col + '_id'
|
col = col + "_id"
|
||||||
|
|
||||||
columns_.append(col)
|
columns_.append(col)
|
||||||
self.operations.append(self.migrator.add_index(
|
self.operations.append(
|
||||||
model._meta.table_name, columns_, unique=unique))
|
self.migrator.add_index(model._meta.table_name, columns_, unique=unique)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
@ -235,13 +248,15 @@ class Migrator(object):
|
|||||||
field.unique = field.index = False
|
field.unique = field.index = False
|
||||||
|
|
||||||
if isinstance(field, peewee.ForeignKeyField):
|
if isinstance(field, peewee.ForeignKeyField):
|
||||||
col = col + '_id'
|
col = col + "_id"
|
||||||
columns_.append(col)
|
columns_.append(col)
|
||||||
index_name = make_index_name(model._meta.table_name, columns_)
|
index_name = make_index_name(model._meta.table_name, columns_)
|
||||||
model._meta.indexes = [(cols, _) for (
|
model._meta.indexes = [
|
||||||
cols, _) in model._meta.indexes if columns != cols]
|
(cols, _) for (cols, _) in model._meta.indexes if columns != cols
|
||||||
self.operations.append(self.migrator.drop_index(
|
]
|
||||||
model._meta.table_name, index_name))
|
self.operations.append(
|
||||||
|
self.migrator.drop_index(model._meta.table_name, index_name)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
@ -250,8 +265,9 @@ class Migrator(object):
|
|||||||
for name in names:
|
for name in names:
|
||||||
field = model._meta.fields[name]
|
field = model._meta.fields[name]
|
||||||
field.null = False
|
field.null = False
|
||||||
self.operations.append(self.migrator.add_not_null(
|
self.operations.append(
|
||||||
model._meta.table_name, field.column_name))
|
self.migrator.add_not_null(model._meta.table_name, field.column_name)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
@ -260,17 +276,21 @@ class Migrator(object):
|
|||||||
for name in names:
|
for name in names:
|
||||||
field = model._meta.fields[name]
|
field = model._meta.fields[name]
|
||||||
field.null = True
|
field.null = True
|
||||||
self.operations.append(self.migrator.drop_not_null(
|
self.operations.append(
|
||||||
model._meta.table_name, field.column_name))
|
self.migrator.drop_not_null(model._meta.table_name, field.column_name)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@get_model
|
@get_model
|
||||||
def add_default(self, model: peewee.Model, name: str, default: t.Any) -> peewee.Model:
|
def add_default(
|
||||||
|
self, model: peewee.Model, name: str, default: t.Any
|
||||||
|
) -> peewee.Model:
|
||||||
"""Add default."""
|
"""Add default."""
|
||||||
field = model._meta.fields[name]
|
field = model._meta.fields[name]
|
||||||
model._meta.defaults[field] = field.default = default
|
model._meta.defaults[field] = field.default = default
|
||||||
self.operations.append(self.migrator.apply_default(
|
self.operations.append(
|
||||||
model._meta.table_name, name, field))
|
self.migrator.apply_default(model._meta.table_name, name, field)
|
||||||
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
@ -278,13 +298,14 @@ class Migrator(object):
|
|||||||
class MigrationManager(object):
|
class MigrationManager(object):
|
||||||
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
|
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
|
||||||
|
|
||||||
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
|
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy], helper):
|
||||||
"""
|
"""
|
||||||
Initializes the migration manager.
|
Initializes the migration manager.
|
||||||
"""
|
"""
|
||||||
if not isinstance(database, (peewee.Database, peewee.Proxy)):
|
if not isinstance(database, (peewee.Database, peewee.Proxy)):
|
||||||
raise RuntimeError('Invalid database: {}'.format(database))
|
raise RuntimeError("Invalid database: {}".format(database))
|
||||||
self.database = database
|
self.database = database
|
||||||
|
self.helper = helper
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def model(self) -> t.Type[MigrateHistory]:
|
def model(self) -> t.Type[MigrateHistory]:
|
||||||
@ -292,7 +313,7 @@ class MigrationManager(object):
|
|||||||
Initialize and cache the MigrationHistory model.
|
Initialize and cache the MigrationHistory model.
|
||||||
"""
|
"""
|
||||||
MigrateHistory._meta.database = self.database
|
MigrateHistory._meta.database = self.database
|
||||||
MigrateHistory._meta.table_name = 'migratehistory'
|
MigrateHistory._meta.table_name = "migratehistory"
|
||||||
MigrateHistory._meta.schema = None
|
MigrateHistory._meta.schema = None
|
||||||
MigrateHistory.create_table(True)
|
MigrateHistory.create_table(True)
|
||||||
return MigrateHistory
|
return MigrateHistory
|
||||||
@ -309,11 +330,18 @@ class MigrationManager(object):
|
|||||||
"""
|
"""
|
||||||
Scans migrations in the file system.
|
Scans migrations in the file system.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(helper.migration_dir):
|
if not os.path.exists(self.helper.migration_dir):
|
||||||
logger.warning('Migration directory: {} does not exist.'.format(
|
logger.warning(
|
||||||
helper.migration_dir))
|
"Migration directory: {} does not exist.".format(
|
||||||
os.makedirs(helper.migration_dir)
|
self.helper.migration_dir
|
||||||
return sorted(f[:-3] for f in os.listdir(helper.migration_dir) if self.filemask.match(f))
|
)
|
||||||
|
)
|
||||||
|
os.makedirs(self.helper.migration_dir)
|
||||||
|
return sorted(
|
||||||
|
f[:-3]
|
||||||
|
for f in os.listdir(self.helper.migration_dir)
|
||||||
|
if self.filemask.match(f)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def diff(self) -> t.List[str]:
|
def diff(self) -> t.List[str]:
|
||||||
@ -333,24 +361,27 @@ class MigrationManager(object):
|
|||||||
self.up_one(name, migrator, True)
|
self.up_one(name, migrator, True)
|
||||||
return migrator
|
return migrator
|
||||||
|
|
||||||
def compile(self, name, migrate='', rollback=''):
|
def compile(self, name, migrate="", rollback=""):
|
||||||
"""
|
"""
|
||||||
Compiles a migration.
|
Compiles a migration.
|
||||||
"""
|
"""
|
||||||
name = datetime.utcnow().strftime('%Y%m%d%H%M%S') + '_' + name
|
name = datetime.utcnow().strftime("%Y%m%d%H%M%S") + "_" + name
|
||||||
filename = name + '.py'
|
filename = name + ".py"
|
||||||
path = os.path.join(helper.migration_dir, filename)
|
path = os.path.join(self.helper.migration_dir, filename)
|
||||||
with open(path, 'w') as f:
|
with open(path, "w") as f:
|
||||||
f.write(MIGRATE_TEMPLATE.format(
|
f.write(
|
||||||
migrate=migrate, rollback=rollback, name=filename))
|
MIGRATE_TEMPLATE.format(
|
||||||
|
migrate=migrate, rollback=rollback, name=filename
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def create(self, name: str = 'auto', auto: bool = False) -> t.Optional[str]:
|
def create(self, name: str = "auto", auto: bool = False) -> t.Optional[str]:
|
||||||
"""
|
"""
|
||||||
Creates a migration.
|
Creates a migration.
|
||||||
"""
|
"""
|
||||||
migrate = rollback = ''
|
migrate = rollback = ""
|
||||||
if auto:
|
if auto:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -367,14 +398,14 @@ class MigrationManager(object):
|
|||||||
"""
|
"""
|
||||||
Runs all unapplied migrations.
|
Runs all unapplied migrations.
|
||||||
"""
|
"""
|
||||||
logger.info('Starting migrations')
|
logger.info("Starting migrations")
|
||||||
console.info('Starting migrations')
|
Console.info("Starting migrations")
|
||||||
|
|
||||||
done = []
|
done = []
|
||||||
diff = self.diff
|
diff = self.diff
|
||||||
if not diff:
|
if not diff:
|
||||||
logger.info('There is nothing to migrate')
|
logger.info("There is nothing to migrate")
|
||||||
console.info('There is nothing to migrate')
|
Console.info("There is nothing to migrate")
|
||||||
return done
|
return done
|
||||||
|
|
||||||
migrator = self.migrator
|
migrator = self.migrator
|
||||||
@ -390,18 +421,23 @@ class MigrationManager(object):
|
|||||||
Reads a migration from a file.
|
Reads a migration from a file.
|
||||||
"""
|
"""
|
||||||
call_params = dict()
|
call_params = dict()
|
||||||
if helper.is_os_windows() and sys.version_info >= (3, 0):
|
if Helpers.is_os_windows() and sys.version_info >= (3, 0):
|
||||||
# if system is windows - force utf-8 encoding
|
# if system is windows - force utf-8 encoding
|
||||||
call_params['encoding'] = 'utf-8'
|
call_params["encoding"] = "utf-8"
|
||||||
with open(os.path.join(helper.migration_dir, name + '.py'), **call_params) as f:
|
with open(
|
||||||
|
os.path.join(self.helper.migration_dir, name + ".py"), **call_params
|
||||||
|
) as f:
|
||||||
code = f.read()
|
code = f.read()
|
||||||
scope = {}
|
scope = {}
|
||||||
code = compile(code, '<string>', 'exec', dont_inherit=True)
|
code = compile(code, "<string>", "exec", dont_inherit=True)
|
||||||
exec(code, scope, None)
|
exec(code, scope, None)
|
||||||
return scope.get('migrate', lambda m, d: None), scope.get('rollback', lambda m, d: None)
|
return scope.get("migrate", lambda m, d: None), scope.get(
|
||||||
|
"rollback", lambda m, d: None
|
||||||
|
)
|
||||||
|
|
||||||
def up_one(self, name: str, migrator: Migrator,
|
def up_one(
|
||||||
fake: bool = False, rollback: bool = False) -> str:
|
self, name: str, migrator: Migrator, fake: bool = False, rollback: bool = False
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a migration with a given name.
|
Runs a migration with a given name.
|
||||||
"""
|
"""
|
||||||
@ -429,8 +465,8 @@ class MigrationManager(object):
|
|||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.database.rollback()
|
self.database.rollback()
|
||||||
operation_name = 'Rollback' if rollback else 'Migration'
|
operation_name = "Rollback" if rollback else "Migration"
|
||||||
logger.exception('{} failed: {}'.format(operation_name, name))
|
logger.exception("{} failed: {}".format(operation_name, name))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def down(self):
|
def down(self):
|
||||||
@ -438,10 +474,10 @@ class MigrationManager(object):
|
|||||||
Rolls back migrations.
|
Rolls back migrations.
|
||||||
"""
|
"""
|
||||||
if not self.done:
|
if not self.done:
|
||||||
raise RuntimeError('No migrations are found.')
|
raise RuntimeError("No migrations are found.")
|
||||||
|
|
||||||
name = self.done[-1]
|
name = self.done[-1]
|
||||||
|
|
||||||
migrator = self.migrator
|
migrator = self.migrator
|
||||||
self.up_one(name, migrator, False, True)
|
self.up_one(name, migrator, False, True)
|
||||||
logger.warning('Rolled back migration: {}'.format(name))
|
logger.warning("Rolled back migration: {}".format(name))
|
||||||
|
@ -1,22 +1,27 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class PermissionHelper:
|
class PermissionHelper:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def both_have_perm(a: str, b: str, permission_tested: Enum):
|
def both_have_perm(
|
||||||
return permission_helper.combine_perm_bool(a[permission_tested.value], b[permission_tested.value])
|
permission_mask_a: str, permission_mask_b: str, permission_tested: Enum
|
||||||
|
):
|
||||||
|
return PermissionHelper.combine_perm_bool(
|
||||||
|
permission_mask_a[permission_tested.value],
|
||||||
|
permission_mask_b[permission_tested.value],
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def combine_perm(a: str, b: str) -> str:
|
def combine_perm(permission_mask_a: str, permission_mask_b: str) -> str:
|
||||||
return '1' if (a == '1' and b == '1') else '0'
|
return "1" if (permission_mask_a == "1" and permission_mask_b == "1") else "0"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def combine_perm_bool(a: str, b: str) -> bool:
|
def combine_perm_bool(permission_mask_a: str, permission_mask_b: str) -> bool:
|
||||||
return a == '1' and b == '1'
|
return permission_mask_a == "1" and permission_mask_b == "1"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str:
|
def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str:
|
||||||
both_masks = zip(list(permission_mask_a), list(permission_mask_b))
|
both_masks = zip(list(permission_mask_a), list(permission_mask_b))
|
||||||
return ''.join(map(lambda x: permission_helper.combine_perm(x[0], x[1]), both_masks))
|
return "".join(
|
||||||
|
map(lambda x: PermissionHelper.combine_perm(x[0], x[1]), both_masks)
|
||||||
|
)
|
||||||
permission_helper = PermissionHelper()
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,71 +4,76 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
|
from tzlocal import get_localzone
|
||||||
|
from apscheduler.events import EVENT_JOB_EXECUTED
|
||||||
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
|
|
||||||
from app.classes.controllers.users_controller import Users_Controller
|
from app.classes.models.management import HelpersManagement
|
||||||
from app.classes.minecraft.serverjars import server_jar_obj
|
from app.classes.models.users import HelperUsers
|
||||||
from app.classes.models.management import management_helper
|
from app.classes.shared.console import Console
|
||||||
from app.classes.models.users import users_helper
|
from app.classes.shared.main_controller import Controller
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.shared.console import console
|
|
||||||
from app.classes.web.tornado_handler import Webserver
|
from app.classes.web.tornado_handler import Webserver
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
|
|
||||||
try:
|
logger = logging.getLogger("apscheduler")
|
||||||
from tzlocal import get_localzone
|
scheduler_intervals = {
|
||||||
from apscheduler.events import EVENT_JOB_EXECUTED
|
"seconds",
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
"minutes",
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
"hours",
|
||||||
|
"days",
|
||||||
|
"weeks",
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
"sunday",
|
||||||
|
}
|
||||||
|
|
||||||
except ModuleNotFoundError as err:
|
|
||||||
helper.auto_installer_fix(err)
|
|
||||||
|
|
||||||
logger = logging.getLogger('apscheduler')
|
|
||||||
scheduler_intervals = { 'seconds',
|
|
||||||
'minutes',
|
|
||||||
'hours',
|
|
||||||
'days',
|
|
||||||
'weeks',
|
|
||||||
'monday',
|
|
||||||
'tuesday',
|
|
||||||
'wednesday',
|
|
||||||
'thursday',
|
|
||||||
'friday',
|
|
||||||
'saturday',
|
|
||||||
'sunday'
|
|
||||||
}
|
|
||||||
|
|
||||||
class TasksManager:
|
class TasksManager:
|
||||||
|
controller: Controller
|
||||||
|
|
||||||
def __init__(self, controller):
|
def __init__(self, helper, controller):
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.tornado = Webserver(controller, self)
|
self.tornado = Webserver(helper, controller, self)
|
||||||
|
|
||||||
self.tz = get_localzone()
|
self.tz = get_localzone()
|
||||||
self.scheduler = BackgroundScheduler(timezone=str(self.tz))
|
self.scheduler = BackgroundScheduler(timezone=str(self.tz))
|
||||||
|
|
||||||
self.users_controller = Users_Controller()
|
self.users_controller = self.controller.users
|
||||||
|
|
||||||
self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread')
|
self.webserver_thread = threading.Thread(
|
||||||
|
target=self.tornado.run_tornado, daemon=True, name="tornado_thread"
|
||||||
|
)
|
||||||
|
|
||||||
self.main_thread_exiting = False
|
self.main_thread_exiting = False
|
||||||
|
|
||||||
self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler")
|
self.schedule_thread = threading.Thread(
|
||||||
|
target=self.scheduler_thread, daemon=True, name="scheduler"
|
||||||
|
)
|
||||||
|
|
||||||
self.log_watcher_thread = threading.Thread(target=self.log_watcher, daemon=True, name="log_watcher")
|
self.log_watcher_thread = threading.Thread(
|
||||||
|
target=self.log_watcher, daemon=True, name="log_watcher"
|
||||||
|
)
|
||||||
|
|
||||||
self.command_thread = threading.Thread(target=self.command_watcher, daemon=True, name="command_watcher")
|
self.command_thread = threading.Thread(
|
||||||
|
target=self.command_watcher, daemon=True, name="command_watcher"
|
||||||
|
)
|
||||||
|
|
||||||
self.realtime_thread = threading.Thread(target=self.realtime, daemon=True, name="realtime")
|
self.realtime_thread = threading.Thread(
|
||||||
|
target=self.realtime, daemon=True, name="realtime"
|
||||||
|
)
|
||||||
|
|
||||||
self.reload_schedule_from_db()
|
self.reload_schedule_from_db()
|
||||||
|
|
||||||
|
|
||||||
def get_main_thread_run_status(self):
|
def get_main_thread_run_status(self):
|
||||||
return self.main_thread_exiting
|
return self.main_thread_exiting
|
||||||
|
|
||||||
def reload_schedule_from_db(self):
|
def reload_schedule_from_db(self):
|
||||||
jobs = management_helper.get_schedules_enabled()
|
jobs = HelpersManagement.get_schedules_enabled()
|
||||||
logger.info("Reload from DB called. Current enabled schedules: ")
|
logger.info("Reload from DB called. Current enabled schedules: ")
|
||||||
for item in jobs:
|
for item in jobs:
|
||||||
logger.info(f"JOB: {item}")
|
logger.info(f"JOB: {item}")
|
||||||
@ -76,26 +81,40 @@ class TasksManager:
|
|||||||
def command_watcher(self):
|
def command_watcher(self):
|
||||||
while True:
|
while True:
|
||||||
# select any commands waiting to be processed
|
# select any commands waiting to be processed
|
||||||
commands = management_helper.get_unactioned_commands()
|
commands = HelpersManagement.get_unactioned_commands()
|
||||||
for c in commands:
|
for cmd in commands:
|
||||||
try:
|
try:
|
||||||
svr = self.controller.get_server_obj(c.server_id)
|
svr = self.controller.get_server_obj(cmd.server_id)
|
||||||
except:
|
except:
|
||||||
logger.error("Server value requested does note exist purging item from waiting commands.")
|
logger.error(
|
||||||
management_helper.mark_command_complete(c.command_id)
|
"Server value requested does note exist! "
|
||||||
|
"Purging item from waiting commands."
|
||||||
|
)
|
||||||
|
HelpersManagement.mark_command_complete(cmd.command_id)
|
||||||
|
|
||||||
user_id = c.user_id
|
user_id = cmd.user_id
|
||||||
command = c.command
|
command = cmd.command
|
||||||
|
|
||||||
if command == 'start_server':
|
if command == "start_server":
|
||||||
svr.run_threaded_server(user_id)
|
svr.run_threaded_server(user_id)
|
||||||
|
|
||||||
elif command == 'stop_server':
|
elif command == "stop_server":
|
||||||
svr.stop_threaded_server()
|
svr.stop_threaded_server()
|
||||||
|
|
||||||
elif command == "restart_server":
|
elif command == "restart_server":
|
||||||
svr.restart_threaded_server(user_id)
|
svr.restart_threaded_server(user_id)
|
||||||
|
|
||||||
|
elif command == "kill_server":
|
||||||
|
try:
|
||||||
|
svr.kill()
|
||||||
|
time.sleep(5)
|
||||||
|
svr.cleanup_server_object()
|
||||||
|
svr.record_server_stats()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
f"Could not find PID for requested termsig. Full error: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
elif command == "backup_server":
|
elif command == "backup_server":
|
||||||
svr.backup_server()
|
svr.backup_server()
|
||||||
|
|
||||||
@ -103,19 +122,19 @@ class TasksManager:
|
|||||||
svr.jar_update()
|
svr.jar_update()
|
||||||
else:
|
else:
|
||||||
svr.send_command(command)
|
svr.send_command(command)
|
||||||
management_helper.mark_command_complete(c.command_id)
|
HelpersManagement.mark_command_complete(cmd.command_id)
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def _main_graceful_exit(self):
|
def _main_graceful_exit(self):
|
||||||
try:
|
try:
|
||||||
os.remove(helper.session_file)
|
os.remove(self.helper.session_file)
|
||||||
self.controller.stop_all_servers()
|
self.controller.stop_all_servers()
|
||||||
except:
|
except:
|
||||||
logger.info("Caught error during shutdown", exc_info=True)
|
logger.info("Caught error during shutdown", exc_info=True)
|
||||||
|
|
||||||
logger.info("***** Crafty Shutting Down *****\n\n")
|
logger.info("***** Crafty Shutting Down *****\n\n")
|
||||||
console.info("***** Crafty Shutting Down *****\n\n")
|
Console.info("***** Crafty Shutting Down *****\n\n")
|
||||||
self.main_thread_exiting = True
|
self.main_thread_exiting = True
|
||||||
|
|
||||||
def start_webserver(self):
|
def start_webserver(self):
|
||||||
@ -123,9 +142,11 @@ class TasksManager:
|
|||||||
|
|
||||||
def reload_webserver(self):
|
def reload_webserver(self):
|
||||||
self.tornado.stop_web_server()
|
self.tornado.stop_web_server()
|
||||||
console.info("Waiting 3 seconds")
|
Console.info("Waiting 3 seconds")
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread')
|
self.webserver_thread = threading.Thread(
|
||||||
|
target=self.tornado.run_tornado, daemon=True, name="tornado_thread"
|
||||||
|
)
|
||||||
self.start_webserver()
|
self.start_webserver()
|
||||||
|
|
||||||
def stop_webserver(self):
|
def stop_webserver(self):
|
||||||
@ -133,79 +154,96 @@ class TasksManager:
|
|||||||
|
|
||||||
def start_scheduler(self):
|
def start_scheduler(self):
|
||||||
logger.info("Launching Scheduler Thread...")
|
logger.info("Launching Scheduler Thread...")
|
||||||
console.info("Launching Scheduler Thread...")
|
Console.info("Launching Scheduler Thread...")
|
||||||
self.schedule_thread.start()
|
self.schedule_thread.start()
|
||||||
logger.info("Launching command thread...")
|
logger.info("Launching command thread...")
|
||||||
console.info("Launching command thread...")
|
Console.info("Launching command thread...")
|
||||||
self.command_thread.start()
|
self.command_thread.start()
|
||||||
logger.info("Launching log watcher...")
|
logger.info("Launching log watcher...")
|
||||||
console.info("Launching log watcher...")
|
Console.info("Launching log watcher...")
|
||||||
self.log_watcher_thread.start()
|
self.log_watcher_thread.start()
|
||||||
logger.info("Launching realtime thread...")
|
logger.info("Launching realtime thread...")
|
||||||
console.info("Launching realtime thread...")
|
Console.info("Launching realtime thread...")
|
||||||
self.realtime_thread.start()
|
self.realtime_thread.start()
|
||||||
|
|
||||||
def scheduler_thread(self):
|
def scheduler_thread(self):
|
||||||
schedules = management_helper.get_schedules_enabled()
|
schedules = HelpersManagement.get_schedules_enabled()
|
||||||
self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED)
|
self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED)
|
||||||
#self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1')
|
# self.scheduler.add_job(
|
||||||
|
# self.scheduler.print_jobs, "interval", seconds=10, id="-1"
|
||||||
|
# )
|
||||||
|
|
||||||
#load schedules from DB
|
# load schedules from DB
|
||||||
for schedule in schedules:
|
for schedule in schedules:
|
||||||
if schedule.interval != 'reaction':
|
if schedule.interval != "reaction":
|
||||||
if schedule.cron_string != "":
|
if schedule.cron_string != "":
|
||||||
try:
|
try:
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
CronTrigger.from_crontab(schedule.cron_string,
|
HelpersManagement.add_command,
|
||||||
timezone=str(self.tz)),
|
CronTrigger.from_crontab(
|
||||||
id = str(schedule.schedule_id),
|
schedule.cron_string, timezone=str(self.tz)
|
||||||
args = [schedule.server_id,
|
),
|
||||||
self.users_controller.get_id_by_name('system'),
|
id=str(schedule.schedule_id),
|
||||||
'127.0.0.1',
|
args=[
|
||||||
schedule.command]
|
schedule.server_id,
|
||||||
)
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
schedule.command,
|
||||||
|
],
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.error(f"Failed to schedule task with error: {e}.")
|
Console.error(f"Failed to schedule task with error: {e}.")
|
||||||
console.warning("Removing failed task from DB.")
|
Console.warning("Removing failed task from DB.")
|
||||||
logger.error(f"Failed to schedule task with error: {e}.")
|
logger.error(f"Failed to schedule task with error: {e}.")
|
||||||
logger.warning("Removing failed task from DB.")
|
logger.warning("Removing failed task from DB.")
|
||||||
#remove items from DB if task fails to add to apscheduler
|
# remove items from DB if task fails to add to apscheduler
|
||||||
management_helper.delete_scheduled_task(schedule.schedule_id)
|
self.controller.management_helper.delete_scheduled_task(
|
||||||
|
schedule.schedule_id
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if schedule.interval_type == 'hours':
|
if schedule.interval_type == "hours":
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
'cron',
|
HelpersManagement.add_command,
|
||||||
minute = 0,
|
"cron",
|
||||||
hour = '*/'+str(schedule.interval),
|
minute=0,
|
||||||
id = str(schedule.schedule_id),
|
hour="*/" + str(schedule.interval),
|
||||||
args = [schedule.server_id,
|
id=str(schedule.schedule_id),
|
||||||
self.users_controller.get_id_by_name('system'),
|
args=[
|
||||||
'127.0.0.1',
|
schedule.server_id,
|
||||||
schedule.command]
|
self.users_controller.get_id_by_name("system"),
|
||||||
)
|
"127.0.0.1",
|
||||||
elif schedule.interval_type == 'minutes':
|
schedule.command,
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
],
|
||||||
'cron',
|
)
|
||||||
minute = '*/'+str(schedule.interval),
|
elif schedule.interval_type == "minutes":
|
||||||
id = str(schedule.schedule_id),
|
self.scheduler.add_job(
|
||||||
args = [schedule.server_id,
|
HelpersManagement.add_command,
|
||||||
self.users_controller.get_id_by_name('system'),
|
"cron",
|
||||||
'127.0.0.1',
|
minute="*/" + str(schedule.interval),
|
||||||
schedule.command]
|
id=str(schedule.schedule_id),
|
||||||
)
|
args=[
|
||||||
elif schedule.interval_type == 'days':
|
schedule.server_id,
|
||||||
curr_time = schedule.start_time.split(':')
|
self.users_controller.get_id_by_name("system"),
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
"127.0.0.1",
|
||||||
'cron',
|
schedule.command,
|
||||||
day = '*/'+str(schedule.interval),
|
],
|
||||||
hour=curr_time[0],
|
)
|
||||||
minute=curr_time[1],
|
elif schedule.interval_type == "days":
|
||||||
id=str(schedule.schedule_id),
|
curr_time = schedule.start_time.split(":")
|
||||||
args=[schedule.server_id,
|
self.scheduler.add_job(
|
||||||
self.users_controller.get_id_by_name('system'),
|
HelpersManagement.add_command,
|
||||||
'127.0.0.1',
|
"cron",
|
||||||
schedule.command]
|
day="*/" + str(schedule.interval),
|
||||||
)
|
hour=curr_time[0],
|
||||||
|
minute=curr_time[1],
|
||||||
|
id=str(schedule.schedule_id),
|
||||||
|
args=[
|
||||||
|
schedule.server_id,
|
||||||
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
schedule.command,
|
||||||
|
],
|
||||||
|
)
|
||||||
self.scheduler.start()
|
self.scheduler.start()
|
||||||
jobs = self.scheduler.get_jobs()
|
jobs = self.scheduler.get_jobs()
|
||||||
logger.info("Loaded schedules. Current enabled schedules: ")
|
logger.info("Loaded schedules. Current enabled schedules: ")
|
||||||
@ -213,240 +251,332 @@ class TasksManager:
|
|||||||
logger.info(f"JOB: {item}")
|
logger.info(f"JOB: {item}")
|
||||||
|
|
||||||
def schedule_job(self, job_data):
|
def schedule_job(self, job_data):
|
||||||
sch_id = management_helper.create_scheduled_task(
|
sch_id = HelpersManagement.create_scheduled_task(
|
||||||
job_data['server_id'],
|
job_data["server_id"],
|
||||||
job_data['action'],
|
job_data["action"],
|
||||||
job_data['interval'],
|
job_data["interval"],
|
||||||
job_data['interval_type'],
|
job_data["interval_type"],
|
||||||
job_data['start_time'],
|
job_data["start_time"],
|
||||||
job_data['command'],
|
job_data["command"],
|
||||||
"None",
|
"None",
|
||||||
job_data['enabled'],
|
job_data["enabled"],
|
||||||
job_data['one_time'],
|
job_data["one_time"],
|
||||||
job_data['cron_string'],
|
job_data["cron_string"],
|
||||||
job_data['parent'],
|
job_data["parent"],
|
||||||
job_data['delay'])
|
job_data["delay"],
|
||||||
#Checks to make sure some doofus didn't actually make the newly created task a child of itself.
|
)
|
||||||
if str(job_data['parent']) == str(sch_id):
|
# Checks to make sure some doofus didn't actually make the newly
|
||||||
management_helper.update_scheduled_task(sch_id, {'parent':None})
|
# created task a child of itself.
|
||||||
#Check to see if it's enabled and is not a chain reaction.
|
if str(job_data["parent"]) == str(sch_id):
|
||||||
if job_data['enabled'] and job_data['interval_type'] != 'reaction':
|
HelpersManagement.update_scheduled_task(sch_id, {"parent": None})
|
||||||
if job_data['cron_string'] != "":
|
# Check to see if it's enabled and is not a chain reaction.
|
||||||
|
if job_data["enabled"] and job_data["interval_type"] != "reaction":
|
||||||
|
if job_data["cron_string"] != "":
|
||||||
try:
|
try:
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
CronTrigger.from_crontab(job_data['cron_string'],
|
HelpersManagement.add_command,
|
||||||
timezone=str(self.tz)),
|
CronTrigger.from_crontab(
|
||||||
id=str(sch_id),
|
job_data["cron_string"], timezone=str(self.tz)
|
||||||
args=[job_data['server_id'],
|
),
|
||||||
self.users_controller.get_id_by_name('system'),
|
id=str(sch_id),
|
||||||
'127.0.0.1',
|
args=[
|
||||||
job_data['command']]
|
job_data["server_id"],
|
||||||
)
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
job_data["command"],
|
||||||
|
],
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.error(f"Failed to schedule task with error: {e}.")
|
Console.error(f"Failed to schedule task with error: {e}.")
|
||||||
console.warning("Removing failed task from DB.")
|
Console.warning("Removing failed task from DB.")
|
||||||
logger.error(f"Failed to schedule task with error: {e}.")
|
logger.error(f"Failed to schedule task with error: {e}.")
|
||||||
logger.warning("Removing failed task from DB.")
|
logger.warning("Removing failed task from DB.")
|
||||||
#remove items from DB if task fails to add to apscheduler
|
# remove items from DB if task fails to add to apscheduler
|
||||||
management_helper.delete_scheduled_task(sch_id)
|
self.controller.management_helper.delete_scheduled_task(sch_id)
|
||||||
else:
|
else:
|
||||||
if job_data['interval_type'] == 'hours':
|
if job_data["interval_type"] == "hours":
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
'cron',
|
HelpersManagement.add_command,
|
||||||
minute = 0,
|
"cron",
|
||||||
hour = '*/'+str(job_data['interval']),
|
minute=0,
|
||||||
id=str(sch_id),
|
hour="*/" + str(job_data["interval"]),
|
||||||
args=[job_data['server_id'],
|
id=str(sch_id),
|
||||||
self.users_controller.get_id_by_name('system'),
|
args=[
|
||||||
'127.0.0.1',
|
job_data["server_id"],
|
||||||
job_data['command']]
|
self.users_controller.get_id_by_name("system"),
|
||||||
)
|
"127.0.0.1",
|
||||||
elif job_data['interval_type'] == 'minutes':
|
job_data["command"],
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
],
|
||||||
'cron',
|
)
|
||||||
minute = '*/'+str(job_data['interval']),
|
elif job_data["interval_type"] == "minutes":
|
||||||
id=str(sch_id),
|
self.scheduler.add_job(
|
||||||
args=[job_data['server_id'],
|
HelpersManagement.add_command,
|
||||||
self.users_controller.get_id_by_name('system'),
|
"cron",
|
||||||
'127.0.0.1',
|
minute="*/" + str(job_data["interval"]),
|
||||||
job_data['command']]
|
id=str(sch_id),
|
||||||
)
|
args=[
|
||||||
elif job_data['interval_type'] == 'days':
|
job_data["server_id"],
|
||||||
curr_time = job_data['start_time'].split(':')
|
self.users_controller.get_id_by_name("system"),
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
"127.0.0.1",
|
||||||
'cron',
|
job_data["command"],
|
||||||
day = '*/'+str(job_data['interval']),
|
],
|
||||||
hour = curr_time[0],
|
)
|
||||||
minute = curr_time[1],
|
elif job_data["interval_type"] == "days":
|
||||||
id=str(sch_id),
|
curr_time = job_data["start_time"].split(":")
|
||||||
args=[job_data['server_id'],
|
self.scheduler.add_job(
|
||||||
self.users_controller.get_id_by_name('system'),
|
HelpersManagement.add_command,
|
||||||
'127.0.0.1',
|
"cron",
|
||||||
job_data['command']],
|
day="*/" + str(job_data["interval"]),
|
||||||
)
|
hour=curr_time[0],
|
||||||
|
minute=curr_time[1],
|
||||||
|
id=str(sch_id),
|
||||||
|
args=[
|
||||||
|
job_data["server_id"],
|
||||||
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
job_data["command"],
|
||||||
|
],
|
||||||
|
)
|
||||||
logger.info("Added job. Current enabled schedules: ")
|
logger.info("Added job. Current enabled schedules: ")
|
||||||
jobs = self.scheduler.get_jobs()
|
jobs = self.scheduler.get_jobs()
|
||||||
for item in jobs:
|
for item in jobs:
|
||||||
logger.info(f"JOB: {item}")
|
logger.info(f"JOB: {item}")
|
||||||
|
|
||||||
def remove_all_server_tasks(self, server_id):
|
def remove_all_server_tasks(self, server_id):
|
||||||
schedules = management_helper.get_schedules_by_server(server_id)
|
schedules = HelpersManagement.get_schedules_by_server(server_id)
|
||||||
for schedule in schedules:
|
for schedule in schedules:
|
||||||
if schedule.interval != 'reaction':
|
if schedule.interval != "reaction":
|
||||||
self.remove_job(schedule.schedule_id)
|
self.remove_job(schedule.schedule_id)
|
||||||
|
|
||||||
def remove_job(self, sch_id):
|
def remove_job(self, sch_id):
|
||||||
job = management_helper.get_scheduled_task_model(sch_id)
|
job = HelpersManagement.get_scheduled_task_model(sch_id)
|
||||||
for schedule in management_helper.get_child_schedules(sch_id):
|
for schedule in HelpersManagement.get_child_schedules(sch_id):
|
||||||
management_helper.update_scheduled_task(schedule.schedule_id, {'parent':None})
|
self.controller.management_helper.update_scheduled_task(
|
||||||
management_helper.delete_scheduled_task(sch_id)
|
schedule.schedule_id, {"parent": None}
|
||||||
if job.enabled and job.interval_type != 'reaction':
|
)
|
||||||
|
self.controller.management_helper.delete_scheduled_task(sch_id)
|
||||||
|
if job.enabled and job.interval_type != "reaction":
|
||||||
self.scheduler.remove_job(str(sch_id))
|
self.scheduler.remove_job(str(sch_id))
|
||||||
logger.info(f"Job with ID {sch_id} was deleted.")
|
logger.info(f"Job with ID {sch_id} was deleted.")
|
||||||
else:
|
else:
|
||||||
logger.info(f"Job with ID {sch_id} was deleted from DB, but was not enabled."
|
logger.info(
|
||||||
+ "Not going to try removing something that doesn't exist from active schedules.")
|
f"Job with ID {sch_id} was deleted from DB, but was not enabled."
|
||||||
|
f"Not going to try removing something "
|
||||||
|
f"that doesn't exist from active schedules."
|
||||||
|
)
|
||||||
|
|
||||||
def update_job(self, sch_id, job_data):
|
def update_job(self, sch_id, job_data):
|
||||||
management_helper.update_scheduled_task(sch_id, job_data)
|
HelpersManagement.update_scheduled_task(sch_id, job_data)
|
||||||
#Checks to make sure some doofus didn't actually make the newly created task a child of itself.
|
# Checks to make sure some doofus didn't actually make the newly
|
||||||
if str(job_data['parent']) == str(sch_id):
|
# created task a child of itself.
|
||||||
management_helper.update_scheduled_task(sch_id, {'parent':None})
|
if str(job_data["parent"]) == str(sch_id):
|
||||||
|
HelpersManagement.update_scheduled_task(sch_id, {"parent": None})
|
||||||
try:
|
try:
|
||||||
if job_data['interval'] != 'reaction':
|
if job_data["interval"] != "reaction":
|
||||||
self.scheduler.remove_job(str(sch_id))
|
self.scheduler.remove_job(str(sch_id))
|
||||||
except:
|
except:
|
||||||
logger.info("No job found in update job. Assuming it was previously disabled. Starting new job.")
|
logger.info(
|
||||||
|
"No job found in update job. "
|
||||||
|
"Assuming it was previously disabled. Starting new job."
|
||||||
|
)
|
||||||
|
|
||||||
if job_data['enabled']:
|
if job_data["enabled"]:
|
||||||
if job_data['interval'] != 'reaction':
|
if job_data["interval"] != "reaction":
|
||||||
if job_data['cron_string'] != "":
|
if job_data["cron_string"] != "":
|
||||||
try:
|
try:
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
CronTrigger.from_crontab(job_data['cron_string'],
|
HelpersManagement.add_command,
|
||||||
timezone=str(self.tz)),
|
CronTrigger.from_crontab(
|
||||||
id=str(sch_id),
|
job_data["cron_string"], timezone=str(self.tz)
|
||||||
args=[job_data['server_id'],
|
),
|
||||||
self.users_controller.get_id_by_name('system'),
|
id=str(sch_id),
|
||||||
'127.0.0.1',
|
args=[
|
||||||
job_data['command']]
|
job_data["server_id"],
|
||||||
)
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
job_data["command"],
|
||||||
|
],
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.error(f"Failed to schedule task with error: {e}.")
|
Console.error(f"Failed to schedule task with error: {e}.")
|
||||||
console.info("Removing failed task from DB.")
|
Console.info("Removing failed task from DB.")
|
||||||
management_helper.delete_scheduled_task(sch_id)
|
self.controller.management_helper.delete_scheduled_task(sch_id)
|
||||||
else:
|
else:
|
||||||
if job_data['interval_type'] == 'hours':
|
if job_data["interval_type"] == "hours":
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
self.scheduler.add_job(
|
||||||
'cron',
|
HelpersManagement.add_command,
|
||||||
minute = 0,
|
"cron",
|
||||||
hour = '*/'+str(job_data['interval']),
|
minute=0,
|
||||||
id=str(sch_id),
|
hour="*/" + str(job_data["interval"]),
|
||||||
args=[job_data['server_id'],
|
id=str(sch_id),
|
||||||
self.users_controller.get_id_by_name('system'),
|
args=[
|
||||||
'127.0.0.1',
|
job_data["server_id"],
|
||||||
job_data['command']]
|
self.users_controller.get_id_by_name("system"),
|
||||||
)
|
"127.0.0.1",
|
||||||
elif job_data['interval_type'] == 'minutes':
|
job_data["command"],
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
],
|
||||||
'cron',
|
)
|
||||||
minute = '*/'+str(job_data['interval']),
|
elif job_data["interval_type"] == "minutes":
|
||||||
id=str(sch_id),
|
self.scheduler.add_job(
|
||||||
args=[job_data['server_id'],
|
HelpersManagement.add_command,
|
||||||
self.users_controller.get_id_by_name('system'),
|
"cron",
|
||||||
'127.0.0.1',
|
minute="*/" + str(job_data["interval"]),
|
||||||
job_data['command']]
|
id=str(sch_id),
|
||||||
)
|
args=[
|
||||||
elif job_data['interval_type'] == 'days':
|
job_data["server_id"],
|
||||||
curr_time = job_data['start_time'].split(':')
|
self.users_controller.get_id_by_name("system"),
|
||||||
self.scheduler.add_job(management_helper.add_command,
|
"127.0.0.1",
|
||||||
'cron',
|
job_data["command"],
|
||||||
day = '*/'+str(job_data['interval']),
|
],
|
||||||
hour = curr_time[0],
|
)
|
||||||
minute = curr_time[1],
|
elif job_data["interval_type"] == "days":
|
||||||
id=str(sch_id),
|
curr_time = job_data["start_time"].split(":")
|
||||||
args=[job_data['server_id'],
|
self.scheduler.add_job(
|
||||||
self.users_controller.get_id_by_name('system'),
|
HelpersManagement.add_command,
|
||||||
'127.0.0.1',
|
"cron",
|
||||||
job_data['command']]
|
day="*/" + str(job_data["interval"]),
|
||||||
)
|
hour=curr_time[0],
|
||||||
|
minute=curr_time[1],
|
||||||
|
id=str(sch_id),
|
||||||
|
args=[
|
||||||
|
job_data["server_id"],
|
||||||
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
job_data["command"],
|
||||||
|
],
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.scheduler.get_job(str(sch_id))
|
self.scheduler.get_job(str(sch_id))
|
||||||
self.scheduler.remove_job(str(sch_id))
|
self.scheduler.remove_job(str(sch_id))
|
||||||
except:
|
except:
|
||||||
logger.info(f"APScheduler found no scheduled job on schedule update for schedule with id: {sch_id} Assuming it was already disabled.")
|
logger.info(
|
||||||
|
f"APScheduler found no scheduled job on schedule update for "
|
||||||
|
f"schedule with id: {sch_id} Assuming it was already disabled."
|
||||||
|
)
|
||||||
|
|
||||||
def schedule_watcher(self, event):
|
def schedule_watcher(self, event):
|
||||||
if not event.exception:
|
if not event.exception:
|
||||||
if str(event.job_id).isnumeric():
|
if str(event.job_id).isnumeric():
|
||||||
task = management_helper.get_scheduled_task_model(int(event.job_id))
|
task = self.controller.management.get_scheduled_task_model(
|
||||||
management_helper.add_to_audit_log_raw('system', users_helper.get_user_id_by_name('system'), task.server_id,
|
int(event.job_id)
|
||||||
f"Task with id {task.schedule_id} completed successfully", '127.0.0.1')
|
)
|
||||||
#check if the task is a single run.
|
self.controller.management.add_to_audit_log_raw(
|
||||||
|
"system",
|
||||||
|
HelperUsers.get_user_id_by_name("system"),
|
||||||
|
task.server_id,
|
||||||
|
f"Task with id {task.schedule_id} completed successfully",
|
||||||
|
"127.0.0.1",
|
||||||
|
)
|
||||||
|
# check if the task is a single run.
|
||||||
if task.one_time:
|
if task.one_time:
|
||||||
self.remove_job(task.schedule_id)
|
self.remove_job(task.schedule_id)
|
||||||
logger.info("one time task detected. Deleting...")
|
logger.info("one time task detected. Deleting...")
|
||||||
#check for any child tasks for this. It's kind of backward, but this makes DB management a lot easier. One to one instead of one to many.
|
# check for any child tasks for this. It's kind of backward,
|
||||||
for schedule in management_helper.get_child_schedules_by_server(task.schedule_id, task.server_id):
|
# but this makes DB management a lot easier. One to one
|
||||||
#event job ID's are strings so we need to look at this as the same data type.
|
# instead of one to many.
|
||||||
|
for schedule in HelpersManagement.get_child_schedules_by_server(
|
||||||
|
task.schedule_id, task.server_id
|
||||||
|
):
|
||||||
|
# event job ID's are strings so we need to look at
|
||||||
|
# this as the same data type.
|
||||||
if str(schedule.parent) == str(event.job_id):
|
if str(schedule.parent) == str(event.job_id):
|
||||||
if schedule.enabled:
|
if schedule.enabled:
|
||||||
delaytime = datetime.datetime.now() + datetime.timedelta(seconds=schedule.delay)
|
delaytime = datetime.datetime.now() + datetime.timedelta(
|
||||||
self.scheduler.add_job(management_helper.add_command, 'date', run_date=delaytime, id=str(schedule.schedule_id),
|
seconds=schedule.delay
|
||||||
args=[schedule.server_id,
|
)
|
||||||
self.users_controller.get_id_by_name('system'),
|
self.scheduler.add_job(
|
||||||
'127.0.0.1',
|
HelpersManagement.add_command,
|
||||||
schedule.command])
|
"date",
|
||||||
|
run_date=delaytime,
|
||||||
|
id=str(schedule.schedule_id),
|
||||||
|
args=[
|
||||||
|
schedule.server_id,
|
||||||
|
self.users_controller.get_id_by_name("system"),
|
||||||
|
"127.0.0.1",
|
||||||
|
schedule.command,
|
||||||
|
],
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.info("Event job ID is not numerical. Assuming it's stats - not stored in DB. Moving on.")
|
logger.info(
|
||||||
|
"Event job ID is not numerical. Assuming it's stats "
|
||||||
|
"- not stored in DB. Moving on."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Task failed with error: {event.exception}")
|
logger.error(f"Task failed with error: {event.exception}")
|
||||||
|
|
||||||
def start_stats_recording(self):
|
def start_stats_recording(self):
|
||||||
stats_update_frequency = helper.get_setting('stats_update_frequency')
|
stats_update_frequency = self.helper.get_setting("stats_update_frequency")
|
||||||
logger.info(f"Stats collection frequency set to {stats_update_frequency} seconds")
|
logger.info(
|
||||||
console.info(f"Stats collection frequency set to {stats_update_frequency} seconds")
|
f"Stats collection frequency set to {stats_update_frequency} seconds"
|
||||||
|
)
|
||||||
|
Console.info(
|
||||||
|
f"Stats collection frequency set to {stats_update_frequency} seconds"
|
||||||
|
)
|
||||||
|
|
||||||
# one for now,
|
# one for now,
|
||||||
self.controller.stats.record_stats()
|
self.controller.stats.record_stats()
|
||||||
# one for later
|
# one for later
|
||||||
self.scheduler.add_job(self.controller.stats.record_stats, 'interval', seconds=stats_update_frequency, id="stats")
|
self.scheduler.add_job(
|
||||||
|
self.controller.stats.record_stats,
|
||||||
|
"interval",
|
||||||
|
seconds=stats_update_frequency,
|
||||||
|
id="stats",
|
||||||
|
)
|
||||||
|
|
||||||
def serverjar_cache_refresher(self):
|
def serverjar_cache_refresher(self):
|
||||||
logger.info("Refreshing serverjars.com cache on start")
|
logger.info("Refreshing serverjars.com cache on start")
|
||||||
server_jar_obj.refresh_cache()
|
self.controller.server_jars.refresh_cache()
|
||||||
|
|
||||||
logger.info("Scheduling Serverjars.com cache refresh service every 12 hours")
|
logger.info("Scheduling Serverjars.com cache refresh service every 12 hours")
|
||||||
self.scheduler.add_job(server_jar_obj.refresh_cache, 'interval', hours=12, id="serverjars")
|
self.scheduler.add_job(
|
||||||
|
self.controller.server_jars.refresh_cache,
|
||||||
|
"interval",
|
||||||
|
hours=12,
|
||||||
|
id="serverjars",
|
||||||
|
)
|
||||||
|
|
||||||
def realtime(self):
|
def realtime(self):
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
host_stats = management_helper.get_latest_hosts_stats()
|
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
if host_stats.get('cpu_usage') != \
|
if host_stats.get(
|
||||||
management_helper.get_latest_hosts_stats().get('cpu_usage') or \
|
"cpu_usage"
|
||||||
host_stats.get('mem_percent') != \
|
) != HelpersManagement.get_latest_hosts_stats().get(
|
||||||
management_helper.get_latest_hosts_stats().get('mem_percent'):
|
"cpu_usage"
|
||||||
|
) or host_stats.get(
|
||||||
|
"mem_percent"
|
||||||
|
) != HelpersManagement.get_latest_hosts_stats().get(
|
||||||
|
"mem_percent"
|
||||||
|
):
|
||||||
# Stats are different
|
# Stats are different
|
||||||
|
|
||||||
host_stats = management_helper.get_latest_hosts_stats()
|
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||||
if len(websocket_helper.clients) > 0:
|
if len(self.helper.websocket_helper.clients) > 0:
|
||||||
# There are clients
|
# There are clients
|
||||||
websocket_helper.broadcast_page('/panel/dashboard', 'update_host_stats', {
|
self.helper.websocket_helper.broadcast_page(
|
||||||
'cpu_usage': host_stats.get('cpu_usage'),
|
"/panel/dashboard",
|
||||||
'cpu_cores': host_stats.get('cpu_cores'),
|
"update_host_stats",
|
||||||
'cpu_cur_freq': host_stats.get('cpu_cur_freq'),
|
{
|
||||||
'cpu_max_freq': host_stats.get('cpu_max_freq'),
|
"cpu_usage": host_stats.get("cpu_usage"),
|
||||||
'mem_percent': host_stats.get('mem_percent'),
|
"cpu_cores": host_stats.get("cpu_cores"),
|
||||||
'mem_usage': host_stats.get('mem_usage')
|
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||||
})
|
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||||
|
"mem_percent": host_stats.get("mem_percent"),
|
||||||
|
"mem_usage": host_stats.get("mem_usage"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
def log_watcher(self):
|
def log_watcher(self):
|
||||||
self.controller.servers.check_for_old_logs()
|
self.controller.servers.check_for_old_logs()
|
||||||
self.scheduler.add_job(self.controller.servers.check_for_old_logs, 'interval', hours=6, id="log-mgmt")
|
self.scheduler.add_job(
|
||||||
|
self.controller.servers.check_for_old_logs,
|
||||||
|
"interval",
|
||||||
|
hours=6,
|
||||||
|
id="log-mgmt",
|
||||||
|
)
|
||||||
|
@ -3,22 +3,25 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Translation:
|
class Translation:
|
||||||
def __init__(self):
|
def __init__(self, helper):
|
||||||
self.translations_path = os.path.join(helper.root_dir, 'app', 'translations')
|
self.helper = helper
|
||||||
|
self.translations_path = os.path.join(
|
||||||
|
self.helper.root_dir, "app", "translations"
|
||||||
|
)
|
||||||
self.cached_translation = None
|
self.cached_translation = None
|
||||||
self.cached_translation_lang = None
|
self.cached_translation_lang = None
|
||||||
|
|
||||||
def get_language_file(self, language: str):
|
def get_language_file(self, language: str):
|
||||||
return os.path.join(self.translations_path, str(language) + '.json')
|
return os.path.join(self.translations_path, str(language) + ".json")
|
||||||
|
|
||||||
def translate(self, page, word, language):
|
def translate(self, page, word, language):
|
||||||
fallback_language = 'en_EN'
|
fallback_language = "en_EN"
|
||||||
|
|
||||||
translated_word = self.translate_inner(page, word, language)
|
translated_word = self.translate_inner(page, word, language)
|
||||||
if translated_word is None:
|
if translated_word is None:
|
||||||
@ -31,20 +34,20 @@ class Translation:
|
|||||||
elif isinstance(translated_word, str):
|
elif isinstance(translated_word, str):
|
||||||
# Basic strings
|
# Basic strings
|
||||||
return translated_word
|
return translated_word
|
||||||
elif hasattr(translated_word, '__iter__'):
|
elif hasattr(translated_word, "__iter__"):
|
||||||
# Multiline strings
|
# Multiline strings
|
||||||
return '\n'.join(translated_word)
|
return "\n".join(translated_word)
|
||||||
return 'Error while getting translation'
|
return "Error while getting translation"
|
||||||
|
|
||||||
def translate_inner(self, page, word, language) -> t.Union[t.Any, None]:
|
def translate_inner(self, page, word, language) -> t.Union[t.Any, None]:
|
||||||
language_file = self.get_language_file(language)
|
language_file = self.get_language_file(language)
|
||||||
try:
|
try:
|
||||||
if not self.cached_translation:
|
if not self.cached_translation:
|
||||||
with open(language_file, 'r', encoding='utf-8') as f:
|
with open(language_file, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self.cached_translation = data
|
self.cached_translation = data
|
||||||
elif self.cached_translation_lang != language:
|
elif self.cached_translation_lang != language:
|
||||||
with open(language_file, 'r', encoding='utf-8') as f:
|
with open(language_file, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self.cached_translation = data
|
self.cached_translation = data
|
||||||
self.cached_translation_lang = language
|
self.cached_translation_lang = language
|
||||||
@ -54,22 +57,35 @@ class Translation:
|
|||||||
try:
|
try:
|
||||||
translated_page = data[page]
|
translated_page = data[page]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error(f'Translation File Error: page {page} does not exist for lang {language}')
|
logger.error(
|
||||||
console.error(f'Translation File Error: page {page} does not exist for lang {language}')
|
f"Translation File Error: page {page} "
|
||||||
|
f"does not exist for lang {language}"
|
||||||
|
)
|
||||||
|
Console.error(
|
||||||
|
f"Translation File Error: page {page} "
|
||||||
|
f"does not exist for lang {language}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
translated_word = translated_page[word]
|
translated_word = translated_page[word]
|
||||||
return translated_word
|
return translated_word
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}')
|
logger.error(
|
||||||
console.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}')
|
f"Translation File Error: word {word} does not exist on page "
|
||||||
|
f"{page} for lang {language}"
|
||||||
|
)
|
||||||
|
Console.error(
|
||||||
|
f"Translation File Error: word {word} does not exist on page "
|
||||||
|
f"{page} for lang {language}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.critical(f'Translation File Error: Unable to read {language_file} due to {e}')
|
logger.critical(
|
||||||
console.critical(f'Translation File Error: Unable to read {language_file} due to {e}')
|
f"Translation File Error: Unable to read {language_file} due to {e}"
|
||||||
|
)
|
||||||
|
Console.critical(
|
||||||
|
f"Translation File Error: Unable to read {language_file} due to {e}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
translation = Translation()
|
|
||||||
|
@ -3,27 +3,20 @@ import html
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import bleach
|
||||||
|
import tornado.web
|
||||||
|
import tornado.escape
|
||||||
|
|
||||||
from app.classes.models.server_permissions import Enum_Permissions_Server
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.translation import translation
|
|
||||||
from app.classes.shared.server import ServerOutBuf
|
from app.classes.shared.server import ServerOutBuf
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import bleach
|
|
||||||
import tornado.web
|
|
||||||
import tornado.escape
|
|
||||||
|
|
||||||
except ModuleNotFoundError as ex:
|
|
||||||
helper.auto_installer_fix(ex)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class AjaxHandler(BaseHandler):
|
|
||||||
|
|
||||||
|
class AjaxHandler(BaseHandler):
|
||||||
def render_page(self, template, page_data):
|
def render_page(self, template, page_data):
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
@ -34,22 +27,19 @@ class AjaxHandler(BaseHandler):
|
|||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def get(self, page):
|
def get(self, page):
|
||||||
_, _, exec_user = self.current_user
|
_, _, exec_user = self.current_user
|
||||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
error = bleach.clean(self.get_argument("error", "WTF Error!"))
|
||||||
|
|
||||||
template = "panel/denied.html"
|
template = "panel/denied.html"
|
||||||
|
|
||||||
page_data = {
|
page_data = {"user_data": exec_user, "error": error}
|
||||||
'user_data': exec_user,
|
|
||||||
'error': error
|
|
||||||
}
|
|
||||||
|
|
||||||
if page == "error":
|
if page == "error":
|
||||||
template = "public/error.html"
|
template = "public/error.html"
|
||||||
self.render_page(template, page_data)
|
self.render_page(template, page_data)
|
||||||
|
|
||||||
elif page == 'server_log':
|
elif page == "server_log":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
full_log = self.get_argument('full', False)
|
full_log = self.get_argument("full", False)
|
||||||
|
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning("Server ID not found in server_log ajax call")
|
logger.warning("Server ID not found in server_log ajax call")
|
||||||
@ -64,50 +54,59 @@ class AjaxHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Server ID Not Found")
|
self.redirect("/panel/error?error=Server ID Not Found")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not server_data['log_path']:
|
if not server_data["log_path"]:
|
||||||
logger.warning(f"Log path not found in server_log ajax call ({server_id})")
|
logger.warning(
|
||||||
|
f"Log path not found in server_log ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
|
||||||
if full_log:
|
if full_log:
|
||||||
log_lines = helper.get_setting('max_log_lines')
|
log_lines = self.helper.get_setting("max_log_lines")
|
||||||
data = helper.tail_file(helper.get_os_understandable_path(server_data['log_path']), log_lines)
|
data = Helpers.tail_file(
|
||||||
|
Helpers.get_os_understandable_path(server_data["log_path"]),
|
||||||
|
log_lines,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
data = ServerOutBuf.lines.get(server_id, [])
|
data = ServerOutBuf.lines.get(server_id, [])
|
||||||
|
|
||||||
|
for line in data:
|
||||||
for d in data:
|
|
||||||
try:
|
try:
|
||||||
d = re.sub('(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )', '', d)
|
line = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)", "", line)
|
||||||
d = re.sub('[A-z]{2}\b\b', '', d)
|
line = re.sub("[A-z]{2}\b\b", "", line)
|
||||||
line = helper.log_colors(html.escape(d))
|
line = self.helper.log_colors(html.escape(line))
|
||||||
self.write(f'{line}<br />')
|
self.write(f"{line}<br />")
|
||||||
# self.write(d.encode("utf-8"))
|
# self.write(d.encode("utf-8"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Skipping Log Line due to error: {e}")
|
logger.warning(f"Skipping Log Line due to error: {e}")
|
||||||
|
|
||||||
elif page == "announcements":
|
elif page == "announcements":
|
||||||
data = helper.get_announcements()
|
data = Helpers.get_announcements()
|
||||||
page_data['notify_data'] = data
|
page_data["notify_data"] = data
|
||||||
self.render_page('ajax/notify.html', page_data)
|
self.render_page("ajax/notify.html", page_data)
|
||||||
|
|
||||||
|
|
||||||
elif page == "get_zip_tree":
|
elif page == "get_zip_tree":
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
|
|
||||||
self.write(helper.get_os_understandable_path(path) + '\n' +
|
self.write(
|
||||||
helper.generate_zip_tree(path))
|
Helpers.get_os_understandable_path(path)
|
||||||
|
+ "\n"
|
||||||
|
+ Helpers.generate_zip_tree(path)
|
||||||
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_zip_dir":
|
elif page == "get_zip_dir":
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
|
|
||||||
self.write(helper.get_os_understandable_path(path) + '\n' +
|
self.write(
|
||||||
helper.generate_zip_dir(path))
|
Helpers.get_os_understandable_path(path)
|
||||||
|
+ "\n"
|
||||||
|
+ Helpers.generate_zip_dir(path)
|
||||||
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_backup_tree":
|
elif page == "get_backup_tree":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
folder = self.get_argument('path', None)
|
folder = self.get_argument("path", None)
|
||||||
|
|
||||||
output = ""
|
output = ""
|
||||||
|
|
||||||
@ -119,31 +118,31 @@ class AjaxHandler(BaseHandler):
|
|||||||
dir_list.append(item)
|
dir_list.append(item)
|
||||||
else:
|
else:
|
||||||
unsorted_files.append(item)
|
unsorted_files.append(item)
|
||||||
file_list = sorted(dir_list, key=str.casefold) + sorted(unsorted_files, key=str.casefold)
|
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||||
output += \
|
unsorted_files, key=str.casefold
|
||||||
f"""<ul class="tree-nested d-block" id="{folder}ul">"""\
|
)
|
||||||
|
output += f"""<ul class="tree-nested d-block" id="{folder}ul">"""
|
||||||
for raw_filename in file_list:
|
for raw_filename in file_list:
|
||||||
filename = html.escape(raw_filename)
|
filename = html.escape(raw_filename)
|
||||||
rel = os.path.join(folder, raw_filename)
|
rel = os.path.join(folder, raw_filename)
|
||||||
dpath = os.path.join(folder, filename)
|
dpath = os.path.join(folder, filename)
|
||||||
if str(dpath) in self.controller.management.get_excluded_backup_dirs(server_id):
|
if str(dpath) in self.controller.management.get_excluded_backup_dirs(
|
||||||
|
server_id
|
||||||
|
):
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += \
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
f"""<li class="tree-item" data-path="{dpath}">
|
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}" checked>
|
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}" checked>
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i class="far fa-folder"></i>
|
<i style="color: #8862e0;" class="far fa-folder"></i>
|
||||||
<i class="far fa-folder-open"></i>
|
<i style="color: #8862e0;" class="far fa-folder-open"></i>
|
||||||
<strong>{filename}</strong>
|
<strong>{filename}</strong>
|
||||||
</span>
|
</span>
|
||||||
</input></div><li>
|
</input></div><li>
|
||||||
\n"""\
|
\n"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="tree-nested d-block tree-ctx-item tree-file"
|
class="d-block tree-ctx-item tree-file"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
data-name="{filename}"
|
data-name="{filename}"
|
||||||
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}" checked><span style="margin-right: 6px;">
|
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}" checked><span style="margin-right: 6px;">
|
||||||
@ -151,32 +150,30 @@ class AjaxHandler(BaseHandler):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += \
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
f"""<li class="tree-item" data-path="{dpath}">
|
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}">
|
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i class="far fa-folder"></i>
|
<i style="color: #8862e0;" class="far fa-folder"></i>
|
||||||
<i class="far fa-folder-open"></i>
|
<i style="color: #8862e0;" class="far fa-folder-open"></i>
|
||||||
<strong>{filename}</strong>
|
<strong>{filename}</strong>
|
||||||
</span>
|
</span>
|
||||||
</input></div><li>
|
</input></div><li>
|
||||||
\n"""\
|
\n"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="tree-nested d-block tree-ctx-item tree-file"
|
class="d-block tree-ctx-item tree-file"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
data-name="{filename}"
|
data-name="{filename}"
|
||||||
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}">
|
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}">
|
||||||
<span style="margin-right: 6px;"><i class="far fa-file"></i></span></input>{filename}</li>"""
|
<span style="margin-right: 6px;"><i class="far fa-file">
|
||||||
self.write(helper.get_os_understandable_path(folder) + '\n' +
|
</i></span></input>{filename}</li>"""
|
||||||
output)
|
self.write(Helpers.get_os_understandable_path(folder) + "\n" + output)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_backup_dir":
|
elif page == "get_backup_dir":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
folder = self.get_argument('path', None)
|
folder = self.get_argument("path", None)
|
||||||
output = ""
|
output = ""
|
||||||
|
|
||||||
dir_list = []
|
dir_list = []
|
||||||
@ -187,39 +184,38 @@ class AjaxHandler(BaseHandler):
|
|||||||
dir_list.append(item)
|
dir_list.append(item)
|
||||||
else:
|
else:
|
||||||
unsorted_files.append(item)
|
unsorted_files.append(item)
|
||||||
file_list = sorted(dir_list, key=str.casefold) + sorted(unsorted_files, key=str.casefold)
|
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||||
output += \
|
unsorted_files, key=str.casefold
|
||||||
f"""<ul class="tree-nested d-block" id="{folder}ul">"""\
|
)
|
||||||
|
output += f"""<ul class="tree-nested d-block" id="{folder}ul">"""
|
||||||
for raw_filename in file_list:
|
for raw_filename in file_list:
|
||||||
filename = html.escape(raw_filename)
|
filename = html.escape(raw_filename)
|
||||||
rel = os.path.join(folder, raw_filename)
|
rel = os.path.join(folder, raw_filename)
|
||||||
dpath = os.path.join(folder, filename)
|
dpath = os.path.join(folder, filename)
|
||||||
if str(dpath) in self.controller.management.get_excluded_backup_dirs(server_id):
|
if str(dpath) in self.controller.management.get_excluded_backup_dirs(
|
||||||
|
server_id
|
||||||
|
):
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += \
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
f"""<li class="tree-item" data-path="{dpath}">
|
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<input type="checkbox" name="root_path" value="{dpath}">
|
<input type="checkbox" name="root_path" value="{dpath}" checked>
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i class="far fa-folder"></i>
|
<i class="far fa-folder"></i>
|
||||||
<i class="far fa-folder-open"></i>
|
<i class="far fa-folder-open"></i>
|
||||||
<strong>{filename}</strong>
|
<strong>{filename}</strong>
|
||||||
</span>
|
</span>
|
||||||
</input></div><li>"""\
|
</input></div><li>"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="tree-item tree-nested d-block tree-ctx-item tree-file"
|
class="tree-item tree-nested d-block tree-ctx-item tree-file"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
data-name="{filename}"
|
data-name="{filename}"
|
||||||
onclick=""><input type='checkbox' name='root_path' value='{dpath}'><span style="margin-right: 6px;">
|
onclick=""><input type='checkbox' name='root_path' value='{dpath}' checked><span style="margin-right: 6px;">
|
||||||
<i class="far fa-file"></i></span></input>{filename}</li>"""
|
<i class="far fa-file"></i></span></input>{filename}</li>"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += \
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
f"""<li class="tree-item" data-path="{dpath}">
|
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<input type="checkbox" name="root_path" value="{dpath}">
|
<input type="checkbox" name="root_path" value="{dpath}">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
@ -227,272 +223,361 @@ class AjaxHandler(BaseHandler):
|
|||||||
<i class="far fa-folder-open"></i>
|
<i class="far fa-folder-open"></i>
|
||||||
<strong>{filename}</strong>
|
<strong>{filename}</strong>
|
||||||
</span>
|
</span>
|
||||||
</input></div><li>"""\
|
</input></div><li>"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="tree-item tree-nested d-block tree-ctx-item tree-file"
|
class="tree-item tree-nested d-block tree-ctx-item tree-file"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
data-name="{filename}"
|
data-name="{filename}"
|
||||||
onclick=""><input type='checkbox' name='root_path' value='{dpath}'>
|
onclick=""><input type='checkbox' name='root_path' value='{dpath}'>
|
||||||
<span style="margin-right: 6px;"><i class="far fa-file"></i></span></input>{filename}</li>"""
|
<span style="margin-right: 6px;"><i class="far fa-file">
|
||||||
|
</i></span></input>{filename}</li>"""
|
||||||
|
|
||||||
self.write(helper.get_os_understandable_path(folder) + '\n' +
|
self.write(Helpers.get_os_understandable_path(folder) + "\n" + output)
|
||||||
output)
|
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_dir":
|
elif page == "get_dir":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'get_tree'):
|
if not self.check_server_id(server_id, "get_tree"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path):
|
if Helpers.validate_traversal(
|
||||||
self.write(helper.get_os_understandable_path(path) + '\n' +
|
self.controller.servers.get_server_data_by_id(server_id)["path"], path
|
||||||
helper.generate_dir(path))
|
):
|
||||||
|
self.write(
|
||||||
|
Helpers.get_os_understandable_path(path)
|
||||||
|
+ "\n"
|
||||||
|
+ Helpers.generate_dir(path)
|
||||||
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def post(self, page):
|
def post(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
|
|
||||||
if page == "send_command":
|
if page == "send_command":
|
||||||
command = self.get_body_argument('command', default=None, strip=True)
|
command = self.get_body_argument("command", default=None, strip=True)
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning("Server ID not found in send_command ajax call")
|
logger.warning("Server ID not found in send_command ajax call")
|
||||||
console.warning("Server ID not found in send_command ajax call")
|
Console.warning("Server ID not found in send_command ajax call")
|
||||||
|
|
||||||
srv_obj = self.controller.get_server_obj(server_id)
|
srv_obj = self.controller.get_server_obj(server_id)
|
||||||
|
|
||||||
if command == srv_obj.settings['stop_command']:
|
if command == srv_obj.settings["stop_command"]:
|
||||||
logger.info("Stop command detected as terminal input - intercepting." +
|
logger.info(
|
||||||
f"Starting Crafty's stop process for server with id: {server_id}")
|
"Stop command detected as terminal input - intercepting."
|
||||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'stop_server')
|
+ f"Starting Crafty's stop process for server with id: {server_id}"
|
||||||
|
)
|
||||||
|
self.controller.management.send_command(
|
||||||
|
exec_user["user_id"], server_id, self.get_remote_ip(), "stop_server"
|
||||||
|
)
|
||||||
command = None
|
command = None
|
||||||
elif command == 'restart':
|
elif command == "restart":
|
||||||
logger.info("Restart command detected as terminal input - intercepting." +
|
logger.info(
|
||||||
f"Starting Crafty's stop process for server with id: {server_id}")
|
"Restart command detected as terminal input - intercepting."
|
||||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'restart_server')
|
+ f"Starting Crafty's stop process for server with id: {server_id}"
|
||||||
|
)
|
||||||
|
self.controller.management.send_command(
|
||||||
|
exec_user["user_id"],
|
||||||
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
"restart_server",
|
||||||
|
)
|
||||||
command = None
|
command = None
|
||||||
if command:
|
if command:
|
||||||
if srv_obj.check_running():
|
if srv_obj.check_running():
|
||||||
srv_obj.send_command(command)
|
srv_obj.send_command(command)
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
self.controller.management.add_to_audit_log(
|
||||||
f"Sent command to {self.controller.servers.get_server_friendly_name(server_id)} terminal: {command}",
|
exec_user["user_id"],
|
||||||
server_id,
|
f"Sent command to "
|
||||||
self.get_remote_ip())
|
f"{self.controller.servers.get_server_friendly_name(server_id)} "
|
||||||
|
f"terminal: {command}",
|
||||||
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
elif page == "send_order":
|
elif page == "send_order":
|
||||||
self.controller.users.update_server_order(exec_user['user_id'], bleach.clean(self.get_argument('order')))
|
self.controller.users.update_server_order(
|
||||||
|
exec_user["user_id"], bleach.clean(self.get_argument("order"))
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
elif page == "backup_now":
|
||||||
|
server_id = self.get_argument("id", None)
|
||||||
|
if server_id is None:
|
||||||
|
logger.error("Server ID is none. Canceling backup!")
|
||||||
|
return
|
||||||
|
|
||||||
|
server = self.controller.get_server_obj(server_id)
|
||||||
|
self.controller.management.add_to_audit_log_raw(
|
||||||
|
self.controller.users.get_user_by_id(exec_user["user_id"])["username"],
|
||||||
|
exec_user["user_id"],
|
||||||
|
server_id,
|
||||||
|
f"Backup now executed for server {server_id} ",
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.backup_server()
|
||||||
|
|
||||||
elif page == "clear_comms":
|
elif page == "clear_comms":
|
||||||
if exec_user['superuser']:
|
if exec_user["superuser"]:
|
||||||
self.controller.clear_unexecuted_commands()
|
self.controller.clear_unexecuted_commands()
|
||||||
return
|
return
|
||||||
|
|
||||||
elif page == "kill":
|
elif page == "kill":
|
||||||
if not permissions['Commands'] in user_perms:
|
if not permissions["Commands"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Commands")
|
self.redirect("/panel/error?error=Unauthorized access to Commands")
|
||||||
return
|
return
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
svr = self.controller.get_server_obj(server_id)
|
svr = self.controller.get_server_obj(server_id)
|
||||||
try:
|
try:
|
||||||
svr.kill()
|
svr.kill()
|
||||||
|
time.sleep(5)
|
||||||
|
svr.cleanup_server_object()
|
||||||
|
svr.record_server_stats()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Could not find PID for requested termsig. Full error: {e}")
|
logger.error(
|
||||||
|
f"Could not find PID for requested termsig. Full error: {e}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
elif page == "eula":
|
elif page == "eula":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
svr = self.controller.get_server_obj(server_id)
|
svr = self.controller.get_server_obj(server_id)
|
||||||
svr.agree_eula(exec_user['user_id'])
|
svr.agree_eula(exec_user["user_id"])
|
||||||
|
|
||||||
elif page == "restore_backup":
|
elif page == "restore_backup":
|
||||||
if not permissions['Backup'] in user_perms:
|
if not permissions["Backup"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||||
return
|
return
|
||||||
server_id = bleach.clean(self.get_argument('id', None))
|
server_id = bleach.clean(self.get_argument("id", None))
|
||||||
zip_name = bleach.clean(self.get_argument('zip_file', None))
|
zip_name = bleach.clean(self.get_argument("zip_file", None))
|
||||||
svr_obj = self.controller.servers.get_server_obj(server_id)
|
svr_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
if server_data['type'] == 'minecraft-java':
|
if server_data["type"] == "minecraft-java":
|
||||||
backup_path = svr_obj.backup_path
|
backup_path = svr_obj.backup_path
|
||||||
if helper.validate_traversal(backup_path, zip_name):
|
if Helpers.validate_traversal(backup_path, zip_name):
|
||||||
tempDir = helper.unzip_backup_archive(backup_path, zip_name)
|
temp_dir = Helpers.unzip_backup_archive(backup_path, zip_name)
|
||||||
new_server = self.controller.import_zip_server(svr_obj.server_name,
|
new_server = self.controller.import_zip_server(
|
||||||
tempDir,
|
svr_obj.server_name,
|
||||||
server_data['executable'],
|
temp_dir,
|
||||||
'1', '2',
|
server_data["executable"],
|
||||||
server_data['server_port'])
|
"1",
|
||||||
|
"2",
|
||||||
|
server_data["server_port"],
|
||||||
|
)
|
||||||
new_server_id = new_server
|
new_server_id = new_server
|
||||||
new_server = self.controller.get_server_data(new_server)
|
new_server = self.controller.get_server_data(new_server)
|
||||||
self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid'])
|
self.controller.rename_backup_dir(
|
||||||
|
server_id, new_server_id, new_server["server_uuid"]
|
||||||
|
)
|
||||||
self.controller.remove_server(server_id, True)
|
self.controller.remove_server(server_id, True)
|
||||||
self.redirect('/panel/dashboard')
|
self.redirect("/panel/dashboard")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
backup_path = svr_obj.backup_path
|
backup_path = svr_obj.backup_path
|
||||||
if helper.validate_traversal(backup_path, zip_name):
|
if Helpers.validate_traversal(backup_path, zip_name):
|
||||||
tempDir = helper.unzip_backup_archive(backup_path, zip_name)
|
temp_dir = Helpers.unzip_backup_archive(backup_path, zip_name)
|
||||||
new_server = self.controller.import_bedrock_zip_server(svr_obj.server_name,
|
new_server = self.controller.import_bedrock_zip_server(
|
||||||
tempDir,
|
svr_obj.server_name,
|
||||||
server_data['executable'],
|
temp_dir,
|
||||||
server_data['server_port'])
|
server_data["executable"],
|
||||||
|
server_data["server_port"],
|
||||||
|
)
|
||||||
new_server_id = new_server
|
new_server_id = new_server
|
||||||
new_server = self.controller.get_server_data(new_server)
|
new_server = self.controller.get_server_data(new_server)
|
||||||
self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid'])
|
self.controller.rename_backup_dir(
|
||||||
|
server_id, new_server_id, new_server["server_uuid"]
|
||||||
|
)
|
||||||
self.controller.remove_server(server_id, True)
|
self.controller.remove_server(server_id, True)
|
||||||
self.redirect('/panel/dashboard')
|
self.redirect("/panel/dashboard")
|
||||||
|
|
||||||
elif page == "unzip_server":
|
elif page == "unzip_server":
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
if helper.check_file_exists(path):
|
if Helpers.check_file_exists(path):
|
||||||
helper.unzipServer(path, exec_user['user_id'])
|
self.helper.unzip_server(path, exec_user["user_id"])
|
||||||
else:
|
else:
|
||||||
user_id = exec_user['user_id']
|
user_id = exec_user["user_id"]
|
||||||
if user_id:
|
if user_id:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
||||||
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
self.helper.websocket_helper.broadcast_user(
|
||||||
'error': translation.translate('error', 'no-file', user_lang)
|
user_id,
|
||||||
})
|
"send_start_error",
|
||||||
|
{
|
||||||
|
"error": self.helper.translation.translate(
|
||||||
|
"error", "no-file", user_lang
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif page == "backup_select":
|
elif page == "backup_select":
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
helper.backup_select(path, exec_user['user_id'])
|
self.helper.backup_select(path, exec_user["user_id"])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def delete(self, page):
|
def delete(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
if page == "del_task":
|
if page == "del_task":
|
||||||
if not permissions['Schedule'] in user_perms:
|
if not permissions["Schedule"] in user_perms:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Tasks")
|
self.redirect("/panel/error?error=Unauthorized access to Tasks")
|
||||||
else:
|
else:
|
||||||
sch_id = self.get_argument('schedule_id', '-404')
|
sch_id = self.get_argument("schedule_id", "-404")
|
||||||
self.tasks_manager.remove_job(sch_id)
|
self.tasks_manager.remove_job(sch_id)
|
||||||
|
|
||||||
if page == "del_backup":
|
if page == "del_backup":
|
||||||
if not permissions['Backup'] in user_perms:
|
if not permissions["Backup"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||||
return
|
return
|
||||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
file_path = Helpers.get_os_understandable_path(
|
||||||
server_id = self.get_argument('id', None)
|
self.get_body_argument("file_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
console.warning(f"Delete {file_path} for server {server_id}")
|
Console.warning(f"Delete {file_path} for server {server_id}")
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'del_backup'):
|
if not self.check_server_id(server_id, "del_backup"):
|
||||||
return
|
return
|
||||||
else: server_id = bleach.clean(server_id)
|
else:
|
||||||
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \
|
if not (
|
||||||
or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \
|
Helpers.in_path(
|
||||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
Helpers.get_os_understandable_path(server_info["path"]), file_path
|
||||||
|
)
|
||||||
|
or Helpers.in_path(
|
||||||
|
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||||
|
file_path,
|
||||||
|
)
|
||||||
|
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||||
logger.warning(f"Invalid path in del_backup ajax call ({file_path})")
|
logger.warning(f"Invalid path in del_backup ajax call ({file_path})")
|
||||||
console.warning(f"Invalid path in del_backup ajax call ({file_path})")
|
Console.warning(f"Invalid path in del_backup ajax call ({file_path})")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Delete the file
|
# Delete the file
|
||||||
if helper.validate_traversal(helper.get_os_understandable_path(server_info['backup_path']), file_path):
|
if Helpers.validate_traversal(
|
||||||
|
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||||
|
file_path,
|
||||||
|
):
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|
||||||
elif page == "delete_server":
|
elif page == "delete_server":
|
||||||
if not permissions['Config'] in user_perms:
|
if not permissions["Config"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||||
return
|
return
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
logger.info(f"Removing server from panel for server: {self.controller.servers.get_server_friendly_name(server_id)}")
|
logger.info(
|
||||||
|
f"Removing server from panel for server: "
|
||||||
|
f"{self.controller.servers.get_server_friendly_name(server_id)}"
|
||||||
|
)
|
||||||
|
|
||||||
server_data = self.controller.get_server_data(server_id)
|
server_data = self.controller.get_server_data(server_id)
|
||||||
server_name = server_data['server_name']
|
server_name = server_data["server_name"]
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
self.controller.management.add_to_audit_log(
|
||||||
f"Deleted server {server_id} named {server_name}",
|
exec_user["user_id"],
|
||||||
server_id,
|
f"Deleted server {server_id} named {server_name}",
|
||||||
self.get_remote_ip())
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
self.controller.remove_server(server_id, False)
|
self.controller.remove_server(server_id, False)
|
||||||
|
|
||||||
elif page == "delete_server_files":
|
elif page == "delete_server_files":
|
||||||
if not permissions['Config'] in user_perms:
|
if not permissions["Config"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||||
return
|
return
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
logger.info(f"Removing server and all associated files for server: {self.controller.servers.get_server_friendly_name(server_id)}")
|
logger.info(
|
||||||
|
f"Removing server and all associated files for server: "
|
||||||
|
f"{self.controller.servers.get_server_friendly_name(server_id)}"
|
||||||
|
)
|
||||||
|
|
||||||
server_data = self.controller.get_server_data(server_id)
|
server_data = self.controller.get_server_data(server_id)
|
||||||
server_name = server_data['server_name']
|
server_name = server_data["server_name"]
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
self.controller.management.add_to_audit_log(
|
||||||
f"Deleted server {server_id} named {server_name}",
|
exec_user["user_id"],
|
||||||
server_id,
|
f"Deleted server {server_id} named {server_name}",
|
||||||
self.get_remote_ip())
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
self.controller.remove_server(server_id, True)
|
self.controller.remove_server(server_id, True)
|
||||||
|
|
||||||
def check_server_id(self, server_id, page_name):
|
def check_server_id(self, server_id, page_name):
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning(f"Server ID not defined in {page_name} ajax call ({server_id})")
|
logger.warning(
|
||||||
console.warning(f"Server ID not defined in {page_name} ajax call ({server_id})")
|
f"Server ID not defined in {page_name} ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Server ID not defined in {page_name} ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
# does this server id exist?
|
# does this server id exist?
|
||||||
if not self.controller.servers.server_id_exists(server_id):
|
if not self.controller.servers.server_id_exists(server_id):
|
||||||
logger.warning(f"Server ID not found in {page_name} ajax call ({server_id})")
|
logger.warning(
|
||||||
console.warning(f"Server ID not found in {page_name} ajax call ({server_id})")
|
f"Server ID not found in {page_name} ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Server ID not found in {page_name} ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
return True
|
return True
|
||||||
|
@ -1,55 +1,93 @@
|
|||||||
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from app.classes.controllers.crafty_perms_controller import EnumPermissionsCrafty
|
||||||
|
from app.classes.controllers.server_perms_controller import EnumPermissionsServer
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
bearer_pattern = re.compile(r'^Bearer', flags=re.IGNORECASE)
|
bearer_pattern = re.compile(r"^Bearer", flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
class ApiHandler(BaseHandler):
|
class ApiHandler(BaseHandler):
|
||||||
|
|
||||||
def return_response(self, status: int, data: dict):
|
def return_response(self, status: int, data: dict):
|
||||||
# Define a standardized response
|
# Define a standardized response
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
self.write(data)
|
self.write(data)
|
||||||
|
|
||||||
def access_denied(self, user, reason=''):
|
def check_xsrf_cookie(self):
|
||||||
|
# Disable CSRF protection on API routes
|
||||||
|
pass
|
||||||
|
|
||||||
|
def access_denied(self, user, reason=""):
|
||||||
if reason:
|
if reason:
|
||||||
reason = ' because ' + reason
|
reason = " because " + reason
|
||||||
logger.info("User %s from IP %s was denied access to the API route " + self.request.path + reason, user, self.get_remote_ip())
|
logger.info(
|
||||||
self.finish(self.return_response(403, {
|
"User %s from IP %s was denied access to the API route "
|
||||||
'error':'ACCESS_DENIED',
|
+ self.request.path
|
||||||
'info':'You were denied access to the requested resource'
|
+ reason,
|
||||||
}))
|
user,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
self.finish(
|
||||||
|
self.return_response(
|
||||||
|
403,
|
||||||
|
{
|
||||||
|
"error": "ACCESS_DENIED",
|
||||||
|
"info": "You were denied access to the requested resource",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def authenticate_user(self) -> bool:
|
def authenticate_user(self) -> bool:
|
||||||
|
self.permissions = {
|
||||||
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
|
"Files": EnumPermissionsServer.FILES,
|
||||||
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
|
"Server_Creation": EnumPermissionsCrafty.SERVER_CREATION,
|
||||||
|
"User_Config": EnumPermissionsCrafty.USER_CONFIG,
|
||||||
|
"Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG,
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
logger.debug("Searching for specified token")
|
logger.debug("Searching for specified token")
|
||||||
|
|
||||||
api_token = self.get_argument('token', '')
|
api_token = self.get_argument("token", "")
|
||||||
if api_token is None and self.request.headers.get('Authorization'):
|
self.api_token = api_token
|
||||||
api_token = bearer_pattern.sub('', self.request.headers.get('Authorization'))
|
if api_token is None and self.request.headers.get("Authorization"):
|
||||||
|
api_token = bearer_pattern.sub(
|
||||||
|
"", self.request.headers.get("Authorization")
|
||||||
|
)
|
||||||
elif api_token is None:
|
elif api_token is None:
|
||||||
api_token = self.get_cookie('token')
|
api_token = self.get_cookie("token")
|
||||||
user_data = self.controller.users.get_user_by_api_token(api_token)
|
user_data = self.controller.users.get_user_by_api_token(api_token)
|
||||||
|
|
||||||
logger.debug("Checking results")
|
logger.debug("Checking results")
|
||||||
if user_data:
|
if user_data:
|
||||||
# Login successful! Check perms
|
# Login successful! Check perms
|
||||||
logger.info(f"User {user_data['username']} has authenticated to API")
|
logger.info(f"User {user_data['username']} has authenticated to API")
|
||||||
# TODO: Role check
|
|
||||||
|
|
||||||
return True # This is to set the "authenticated"
|
return True # This is to set the "authenticated"
|
||||||
else:
|
else:
|
||||||
logging.debug("Auth unsuccessful")
|
logging.debug("Auth unsuccessful")
|
||||||
self.access_denied("unknown", "the user provided an invalid token")
|
self.access_denied("unknown", "the user provided an invalid token")
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("An error occured while authenticating an API user: %s", e)
|
logger.warning("An error occured while authenticating an API user: %s", e)
|
||||||
self.finish(self.return_response(403, {
|
self.finish(
|
||||||
'error':'ACCESS_DENIED',
|
self.return_response(
|
||||||
'info':'An error occured while authenticating the user'
|
403,
|
||||||
}))
|
{
|
||||||
|
"error": "ACCESS_DENIED",
|
||||||
|
"info": "An error occured while authenticating the user",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -57,12 +95,28 @@ class ServersStats(ApiHandler):
|
|||||||
def get(self):
|
def get(self):
|
||||||
"""Get details about all servers"""
|
"""Get details about all servers"""
|
||||||
authenticated = self.authenticate_user()
|
authenticated = self.authenticate_user()
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
if not authenticated:
|
if not authenticated:
|
||||||
return
|
return
|
||||||
|
if user_obj["superuser"]:
|
||||||
|
raw_stats = self.controller.servers.get_all_servers_stats()
|
||||||
|
else:
|
||||||
|
raw_stats = self.controller.servers.get_authorized_servers_stats(
|
||||||
|
user_obj["user_id"]
|
||||||
|
)
|
||||||
|
stats = []
|
||||||
|
for rs in raw_stats:
|
||||||
|
s = {}
|
||||||
|
for k, v in rs["server_data"].items():
|
||||||
|
if isinstance(v, datetime):
|
||||||
|
s[k] = v.timestamp()
|
||||||
|
else:
|
||||||
|
s[k] = v
|
||||||
|
stats.append(s)
|
||||||
|
|
||||||
# Get server stats
|
# Get server stats
|
||||||
# TODO Check perms
|
# TODO Check perms
|
||||||
self.finish(self.write({"servers": self.controller.stats.get_servers_stats()}))
|
self.finish(self.write({"servers": stats}))
|
||||||
|
|
||||||
|
|
||||||
class NodeStats(ApiHandler):
|
class NodeStats(ApiHandler):
|
||||||
@ -74,5 +128,311 @@ class NodeStats(ApiHandler):
|
|||||||
|
|
||||||
# Get node stats
|
# Get node stats
|
||||||
node_stats = self.controller.stats.get_node_stats()
|
node_stats = self.controller.stats.get_node_stats()
|
||||||
node_stats.pop("servers")
|
self.return_response(200, {"code": node_stats["node_stats"]})
|
||||||
self.finish(self.write(node_stats))
|
|
||||||
|
|
||||||
|
class SendCommand(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
|
||||||
|
if (
|
||||||
|
not user_obj["user_id"]
|
||||||
|
in self.controller.server_perms.get_server_user_list(server_id)
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"Commands"
|
||||||
|
] in self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token), server_id
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
return
|
||||||
|
|
||||||
|
command = self.get_argument("command", default=None, strip=True)
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
if command:
|
||||||
|
server = self.controller.get_server_obj(server_id)
|
||||||
|
if server.check_running:
|
||||||
|
server.send_command(command)
|
||||||
|
self.return_response(200, {"run": True})
|
||||||
|
else:
|
||||||
|
self.return_response(200, {"error": "SER_NOT_RUNNING"})
|
||||||
|
else:
|
||||||
|
self.return_response(200, {"error": "NO_COMMAND"})
|
||||||
|
|
||||||
|
|
||||||
|
class ServerBackup(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
|
||||||
|
if (
|
||||||
|
not user_obj["user_id"]
|
||||||
|
in self.controller.server_perms.get_server_user_list(server_id)
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"Backup"
|
||||||
|
] in self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token), server_id
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
return
|
||||||
|
|
||||||
|
server = self.controller.get_server_obj(server_id)
|
||||||
|
|
||||||
|
server.backup_server()
|
||||||
|
|
||||||
|
self.return_response(200, {"code": "SER_BAK_CALLED"})
|
||||||
|
|
||||||
|
|
||||||
|
class StartServer(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
remote_ip = self.get_remote_ip()
|
||||||
|
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
|
||||||
|
if (
|
||||||
|
not user_obj["user_id"]
|
||||||
|
in self.controller.server_perms.get_server_user_list(server_id)
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
elif not self.permissions[
|
||||||
|
"Commands"
|
||||||
|
] in self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token), server_id
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
server = self.controller.get_server_obj(server_id)
|
||||||
|
|
||||||
|
if not server.check_running():
|
||||||
|
self.controller.management.send_command(
|
||||||
|
user_obj["user_id"], server_id, remote_ip, "start_server"
|
||||||
|
)
|
||||||
|
self.return_response(200, {"code": "SER_START_CALLED"})
|
||||||
|
else:
|
||||||
|
self.return_response(500, {"error": "SER_RUNNING"})
|
||||||
|
|
||||||
|
|
||||||
|
class StopServer(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
remote_ip = self.get_remote_ip()
|
||||||
|
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
|
||||||
|
if (
|
||||||
|
not user_obj["user_id"]
|
||||||
|
in self.controller.server_perms.get_server_user_list(server_id)
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"Commands"
|
||||||
|
] in self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token), server_id
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
return
|
||||||
|
|
||||||
|
server = self.controller.get_server_obj(server_id)
|
||||||
|
|
||||||
|
if server.check_running():
|
||||||
|
self.controller.management.send_command(
|
||||||
|
user, server_id, remote_ip, "stop_server"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.return_response(200, {"code": "SER_STOP_CALLED"})
|
||||||
|
else:
|
||||||
|
self.return_response(500, {"error": "SER_NOT_RUNNING"})
|
||||||
|
|
||||||
|
|
||||||
|
class RestartServer(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
remote_ip = self.get_remote_ip()
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
server_id = self.get_argument("id")
|
||||||
|
|
||||||
|
if not user_obj["user_id"] in self.controller.server_perms.get_server_user_list(
|
||||||
|
server_id
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"Commands"
|
||||||
|
] in self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token), server_id
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
|
||||||
|
self.controller.management.send_command(
|
||||||
|
user, server_id, remote_ip, "restart_server"
|
||||||
|
)
|
||||||
|
self.return_response(200, {"code": "SER_RESTART_CALLED"})
|
||||||
|
|
||||||
|
|
||||||
|
class CreateUser(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
user_perms = self.controller.crafty_perms.get_crafty_permissions_list(
|
||||||
|
user_obj["user_id"]
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
not self.permissions["User_Config"] in user_perms
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"User_Config"
|
||||||
|
] in self.controller.crafty_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token)
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
return
|
||||||
|
|
||||||
|
new_username = self.get_argument("username")
|
||||||
|
new_pass = self.get_argument("password")
|
||||||
|
|
||||||
|
if new_username:
|
||||||
|
self.controller.users.add_user(
|
||||||
|
new_username, new_pass, "default@example.com", True, False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.return_response(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"code": "COMPLETE",
|
||||||
|
"username": new_username,
|
||||||
|
"password": new_pass,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.return_response(
|
||||||
|
500,
|
||||||
|
{
|
||||||
|
"error": "MISSING_PARAMS",
|
||||||
|
"info": "Some paramaters failed validation",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteUser(ApiHandler):
|
||||||
|
def post(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
user_perms = self.controller.crafty_perms.get_crafty_permissions_list(
|
||||||
|
user_obj["user_id"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
not self.permissions["User_Config"] in user_perms
|
||||||
|
and not user_obj["superuser"]
|
||||||
|
):
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.permissions[
|
||||||
|
"User_Config"
|
||||||
|
] in self.controller.crafty_perms.get_api_key_permissions_list(
|
||||||
|
self.controller.users.get_api_key_by_token(self.api_token)
|
||||||
|
):
|
||||||
|
self.access_denied(user)
|
||||||
|
return
|
||||||
|
|
||||||
|
user_id = self.get_argument("user_id", None, True)
|
||||||
|
user_to_del = self.controller.users.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
if user_to_del["superuser"]:
|
||||||
|
self.return_response(
|
||||||
|
500,
|
||||||
|
{"error": "NOT_ALLOWED", "info": "You cannot delete a super user"},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if user_id:
|
||||||
|
self.controller.users.remove_user(user_id)
|
||||||
|
self.return_response(200, {"code": "COMPLETED"})
|
||||||
|
|
||||||
|
|
||||||
|
class ListServers(ApiHandler):
|
||||||
|
def get(self):
|
||||||
|
user = self.authenticate_user()
|
||||||
|
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.api_token is None:
|
||||||
|
self.access_denied("unknown")
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_obj["superuser"]:
|
||||||
|
servers = self.controller.servers.get_all_defined_servers()
|
||||||
|
servers = [str(i) for i in servers]
|
||||||
|
else:
|
||||||
|
servers = self.controller.servers.get_authorized_servers(
|
||||||
|
user_obj["user_id"]
|
||||||
|
)
|
||||||
|
servers = [str(i) for i in servers]
|
||||||
|
|
||||||
|
self.return_response(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"code": "COMPLETED",
|
||||||
|
"servers": servers,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
23
app/classes/web/base_api_handler.py
Normal file
23
app/classes/web/base_api_handler.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from typing import Awaitable, Callable, Optional
|
||||||
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
class BaseApiHandler(BaseHandler):
|
||||||
|
# {{{ Disable XSRF protection on API routes
|
||||||
|
def check_xsrf_cookie(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ 405 Method Not Allowed as JSON
|
||||||
|
def _unimplemented_method(self, *_args: str, **_kwargs: str) -> None:
|
||||||
|
self.finish_json(405, {"status": "error", "error": "METHOD_NOT_ALLOWED"})
|
||||||
|
|
||||||
|
head = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
get = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
post = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
delete = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
patch = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
put = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
options = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
# }}}
|
@ -1,44 +1,87 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import (
|
import re
|
||||||
Union,
|
import typing as t
|
||||||
List,
|
import orjson
|
||||||
Optional, Tuple, Dict, Any
|
import bleach
|
||||||
)
|
import tornado.web
|
||||||
|
|
||||||
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
from app.classes.models.users import ApiKeys
|
from app.classes.models.users import ApiKeys
|
||||||
from app.classes.shared.authentication import authentication
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.main_controller import Controller
|
from app.classes.shared.main_controller import Controller
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.translation import Translation
|
||||||
|
|
||||||
try:
|
|
||||||
import tornado.web
|
|
||||||
import bleach
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
bearer_pattern = re.compile(r"^Bearer ", flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
class BaseHandler(tornado.web.RequestHandler):
|
class BaseHandler(tornado.web.RequestHandler):
|
||||||
|
def set_default_headers(self) -> None:
|
||||||
|
"""
|
||||||
|
Fix CORS
|
||||||
|
"""
|
||||||
|
self.set_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.set_header(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Content-Type, x-requested-with, Authorization",
|
||||||
|
)
|
||||||
|
self.set_header(
|
||||||
|
"Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS"
|
||||||
|
)
|
||||||
|
|
||||||
|
def options(self, *_, **__):
|
||||||
|
"""
|
||||||
|
Fix CORS
|
||||||
|
"""
|
||||||
|
# no body
|
||||||
|
self.set_status(204)
|
||||||
|
self.finish()
|
||||||
|
|
||||||
nobleach = {bool, type(None)}
|
nobleach = {bool, type(None)}
|
||||||
redactables = ("pass", "api")
|
redactables = ("pass", "api")
|
||||||
|
|
||||||
|
helper: Helpers
|
||||||
|
controller: Controller
|
||||||
|
translator: Translation
|
||||||
|
|
||||||
# noinspection PyAttributeOutsideInit
|
# noinspection PyAttributeOutsideInit
|
||||||
def initialize(self, controller: Controller = None, tasks_manager=None, translator=None):
|
def initialize(
|
||||||
|
self, helper=None, controller=None, tasks_manager=None, translator=None
|
||||||
|
):
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.tasks_manager = tasks_manager
|
self.tasks_manager = tasks_manager
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
|
|
||||||
def get_remote_ip(self):
|
def get_remote_ip(self):
|
||||||
remote_ip = self.request.headers.get("X-Real-IP") or \
|
remote_ip = (
|
||||||
self.request.headers.get("X-Forwarded-For") or \
|
self.request.headers.get("X-Real-IP")
|
||||||
self.request.remote_ip
|
or self.request.headers.get("X-Forwarded-For")
|
||||||
|
or self.request.remote_ip
|
||||||
|
)
|
||||||
return remote_ip
|
return remote_ip
|
||||||
|
|
||||||
current_user: Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]
|
current_user: t.Tuple[t.Optional[ApiKeys], t.Dict[str, t.Any], t.Dict[str, t.Any]]
|
||||||
def get_current_user(self) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
"""
|
||||||
return authentication.check(self.get_cookie("token"))
|
A variable that contains the current user's data. Please see
|
||||||
|
Please only use this with routes using the `@tornado.web.authenticated` decorator.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_current_user(
|
||||||
|
self,
|
||||||
|
) -> t.Optional[
|
||||||
|
t.Tuple[t.Optional[ApiKeys], t.Dict[str, t.Any], t.Dict[str, t.Any]]
|
||||||
|
]:
|
||||||
|
"""
|
||||||
|
Get the token's API key, the token's payload and user data.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
t.Optional[ApiKeys]: The API key of the token.
|
||||||
|
t.Dict[str, t.Any]: The token's payload.
|
||||||
|
t.Dict[str, t.Any]: The user's data from the database.
|
||||||
|
"""
|
||||||
|
return self.controller.authentication.check(self.get_cookie("token"))
|
||||||
|
|
||||||
def autobleach(self, name, text):
|
def autobleach(self, name, text):
|
||||||
for r in self.redactables:
|
for r in self.redactables:
|
||||||
@ -54,15 +97,17 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||||||
return bleach.clean(text)
|
return bleach.clean(text)
|
||||||
|
|
||||||
def get_argument(
|
def get_argument(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
default: Union[None, str, tornado.web._ArgDefaultMarker] = tornado.web._ARG_DEFAULT,
|
default: t.Union[
|
||||||
strip: bool = True,
|
None, str, tornado.web._ArgDefaultMarker
|
||||||
) -> Optional[str]:
|
] = tornado.web._ARG_DEFAULT,
|
||||||
|
strip: bool = True,
|
||||||
|
) -> t.Optional[str]:
|
||||||
arg = self._get_argument(name, default, self.request.arguments, strip)
|
arg = self._get_argument(name, default, self.request.arguments, strip)
|
||||||
return self.autobleach(name, arg)
|
return self.autobleach(name, arg)
|
||||||
|
|
||||||
def get_arguments(self, name: str, strip: bool = True) -> List[str]:
|
def get_arguments(self, name: str, strip: bool = True) -> t.List[str]:
|
||||||
if not isinstance(strip, bool):
|
if not isinstance(strip, bool):
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
args = self._get_arguments(name, self.request.arguments, strip)
|
args = self._get_arguments(name, self.request.arguments, strip)
|
||||||
@ -70,3 +115,127 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||||||
for arg in args:
|
for arg in args:
|
||||||
args_ret += self.autobleach(name, arg)
|
args_ret += self.autobleach(name, arg)
|
||||||
return args_ret
|
return args_ret
|
||||||
|
|
||||||
|
def access_denied(self, user: t.Optional[str], reason: t.Optional[str]):
|
||||||
|
ip = self.get_remote_ip()
|
||||||
|
route = self.request.path
|
||||||
|
if user is not None:
|
||||||
|
user_data = f"User {user} from IP {ip}"
|
||||||
|
else:
|
||||||
|
user_data = f"An unknown user from IP {ip}"
|
||||||
|
if reason:
|
||||||
|
ending = f"to the API route {route} because {reason}"
|
||||||
|
else:
|
||||||
|
ending = f"to the API route {route}"
|
||||||
|
logger.info(f"{user_data} was denied access {ending}")
|
||||||
|
self.finish_json(
|
||||||
|
403,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "ACCESS_DENIED",
|
||||||
|
"info": "You were denied access to the requested resource",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _auth_get_api_token(self) -> t.Optional[str]:
|
||||||
|
"""Get an API token from the request
|
||||||
|
|
||||||
|
The API token is searched in the following order:
|
||||||
|
1. The `token` query parameter
|
||||||
|
2. The `Authorization` header
|
||||||
|
3. The `token` cookie
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
t.Optional[str]: The API token or None if no token was found.
|
||||||
|
"""
|
||||||
|
logger.debug("Searching for specified token")
|
||||||
|
api_token = self.get_query_argument("token", None)
|
||||||
|
if api_token is None and self.request.headers.get("Authorization"):
|
||||||
|
api_token = bearer_pattern.sub(
|
||||||
|
"", self.request.headers.get("Authorization")
|
||||||
|
)
|
||||||
|
elif api_token is None:
|
||||||
|
api_token = self.get_cookie("token")
|
||||||
|
return api_token
|
||||||
|
|
||||||
|
def authenticate_user(
|
||||||
|
self,
|
||||||
|
) -> t.Optional[
|
||||||
|
t.Tuple[
|
||||||
|
t.List,
|
||||||
|
t.List[EnumPermissionsCrafty],
|
||||||
|
t.List[str],
|
||||||
|
bool,
|
||||||
|
t.Dict[str, t.Any],
|
||||||
|
]
|
||||||
|
]:
|
||||||
|
try:
|
||||||
|
api_key, _token_data, user = self.controller.authentication.check_err(
|
||||||
|
self._auth_get_api_token()
|
||||||
|
)
|
||||||
|
|
||||||
|
superuser = user["superuser"]
|
||||||
|
if api_key is not None:
|
||||||
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
|
exec_user_role = set()
|
||||||
|
if superuser:
|
||||||
|
authorized_servers = self.controller.list_defined_servers()
|
||||||
|
exec_user_role.add("Super User")
|
||||||
|
exec_user_crafty_permissions = (
|
||||||
|
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if api_key is not None:
|
||||||
|
exec_user_crafty_permissions = (
|
||||||
|
self.controller.crafty_perms.get_api_key_permissions_list(
|
||||||
|
api_key
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
exec_user_crafty_permissions = (
|
||||||
|
self.controller.crafty_perms.get_crafty_permissions_list(
|
||||||
|
user["user_id"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.debug(user["roles"])
|
||||||
|
for r in user["roles"]:
|
||||||
|
role = self.controller.roles.get_role(r)
|
||||||
|
exec_user_role.add(role["role_name"])
|
||||||
|
authorized_servers = self.controller.servers.get_authorized_servers(
|
||||||
|
user["user_id"] # TODO: API key authorized servers?
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Checking results")
|
||||||
|
if user:
|
||||||
|
return (
|
||||||
|
authorized_servers,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
exec_user_role,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.debug("Auth unsuccessful")
|
||||||
|
self.access_denied(None, "the user provided an invalid token")
|
||||||
|
return None
|
||||||
|
except Exception as auth_exception:
|
||||||
|
logger.debug(
|
||||||
|
"An error occured while authenticating an API user:",
|
||||||
|
exc_info=auth_exception,
|
||||||
|
)
|
||||||
|
self.finish_json(
|
||||||
|
403,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "ACCESS_DENIED",
|
||||||
|
"info": "An error occured while authenticating the user",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def finish_json(self, status: int, data: t.Dict[str, t.Any]):
|
||||||
|
self.set_status(status)
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
self.finish(orjson.dumps(data)) # pylint: disable=no-member
|
||||||
|
@ -4,11 +4,11 @@ from app.classes.web.base_handler import BaseHandler
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DefaultHandler(BaseHandler):
|
class DefaultHandler(BaseHandler):
|
||||||
|
|
||||||
# Override prepare() instead of get() to cover all possible HTTP methods.
|
# Override prepare() instead of get() to cover all possible HTTP methods.
|
||||||
# pylint: disable=arguments-differ
|
def prepare(self, page=None): # pylint: disable=arguments-differ
|
||||||
def prepare(self, page=None):
|
|
||||||
if page is not None:
|
if page is not None:
|
||||||
self.set_status(404)
|
self.set_status(404)
|
||||||
self.render(
|
self.render(
|
||||||
@ -18,5 +18,5 @@ class DefaultHandler(BaseHandler):
|
|||||||
else:
|
else:
|
||||||
self.redirect(
|
self.redirect(
|
||||||
"/public/login",
|
"/public/login",
|
||||||
#translate=self.translator.translate,
|
# translate=self.translator.translate,
|
||||||
)
|
)
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import bleach
|
||||||
|
import tornado.web
|
||||||
|
import tornado.escape
|
||||||
|
|
||||||
from app.classes.models.server_permissions import Enum_Permissions_Server
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.file_helpers import file_helper
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import bleach
|
|
||||||
import tornado.web
|
|
||||||
import tornado.escape
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class FileHandler(BaseHandler):
|
|
||||||
|
|
||||||
|
class FileHandler(BaseHandler):
|
||||||
def render_page(self, template, page_data):
|
def render_page(self, template, page_data):
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
@ -29,370 +24,480 @@ class FileHandler(BaseHandler):
|
|||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def get(self, page):
|
def get(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
|
|
||||||
if page == "get_file":
|
if page == "get_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
file_path = helper.get_os_understandable_path(self.get_argument('file_path', None))
|
file_path = Helpers.get_os_understandable_path(
|
||||||
|
self.get_argument("file_path", None)
|
||||||
|
)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'get_file'):
|
if not self.check_server_id(server_id, "get_file"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\
|
if not Helpers.in_path(
|
||||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid path in get_file file file ajax call ({file_path})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid path in get_file file file ajax call ({file_path})")
|
),
|
||||||
|
file_path,
|
||||||
|
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid path in get_file file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid path in get_file file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
error = None
|
error = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(file_path, encoding='utf-8') as file:
|
with open(file_path, encoding="utf-8") as file:
|
||||||
file_contents = file.read()
|
file_contents = file.read()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
file_contents = ''
|
file_contents = ""
|
||||||
error = 'UnicodeDecodeError'
|
error = "UnicodeDecodeError"
|
||||||
|
|
||||||
self.write({
|
self.write({"content": file_contents, "error": error})
|
||||||
'content': file_contents,
|
|
||||||
'error': error
|
|
||||||
})
|
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_tree":
|
elif page == "get_tree":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'get_tree'):
|
if not self.check_server_id(server_id, "get_tree"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path):
|
if Helpers.validate_traversal(
|
||||||
self.write(helper.get_os_understandable_path(path) + '\n' +
|
self.controller.servers.get_server_data_by_id(server_id)["path"], path
|
||||||
helper.generate_tree(path))
|
):
|
||||||
|
self.write(
|
||||||
|
Helpers.get_os_understandable_path(path)
|
||||||
|
+ "\n"
|
||||||
|
+ Helpers.generate_tree(path)
|
||||||
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "get_dir":
|
elif page == "get_dir":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
path = self.get_argument('path', None)
|
path = self.get_argument("path", None)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'get_tree'):
|
if not self.check_server_id(server_id, "get_tree"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path):
|
if Helpers.validate_traversal(
|
||||||
self.write(helper.get_os_understandable_path(path) + '\n' +
|
self.controller.servers.get_server_data_by_id(server_id)["path"], path
|
||||||
helper.generate_dir(path))
|
):
|
||||||
|
self.write(
|
||||||
|
Helpers.get_os_understandable_path(path)
|
||||||
|
+ "\n"
|
||||||
|
+ Helpers.generate_dir(path)
|
||||||
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def post(self, page):
|
def post(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
|
|
||||||
if page == "create_file":
|
if page == "create_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True))
|
file_parent = Helpers.get_os_understandable_path(
|
||||||
file_name = self.get_body_argument('file_name', default=None, strip=True)
|
self.get_body_argument("file_parent", default=None, strip=True)
|
||||||
|
)
|
||||||
|
file_name = self.get_body_argument("file_name", default=None, strip=True)
|
||||||
file_path = os.path.join(file_parent, file_name)
|
file_path = os.path.join(file_parent, file_name)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'create_file'):
|
if not self.check_server_id(server_id, "create_file"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path) \
|
if not Helpers.in_path(
|
||||||
or helper.check_file_exists(os.path.abspath(file_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid path in create_file file ajax call ({file_path})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid path in create_file file ajax call ({file_path})")
|
),
|
||||||
|
file_path,
|
||||||
|
) or Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid path in create_file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid path in create_file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create the file by opening it
|
# Create the file by opening it
|
||||||
with open(file_path, 'w', encoding='utf-8') as file_object:
|
with open(file_path, "w", encoding="utf-8") as file_object:
|
||||||
file_object.close()
|
file_object.close()
|
||||||
|
|
||||||
elif page == "create_dir":
|
elif page == "create_dir":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True))
|
dir_parent = Helpers.get_os_understandable_path(
|
||||||
dir_name = self.get_body_argument('dir_name', default=None, strip=True)
|
self.get_body_argument("dir_parent", default=None, strip=True)
|
||||||
|
)
|
||||||
|
dir_name = self.get_body_argument("dir_name", default=None, strip=True)
|
||||||
dir_path = os.path.join(dir_parent, dir_name)
|
dir_path = os.path.join(dir_parent, dir_name)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'create_dir'):
|
if not self.check_server_id(server_id, "create_dir"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), dir_path) \
|
if not Helpers.in_path(
|
||||||
or helper.check_path_exists(os.path.abspath(dir_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid path in create_dir file ajax call ({dir_path})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid path in create_dir file ajax call ({dir_path})")
|
),
|
||||||
|
dir_path,
|
||||||
|
) or Helpers.check_path_exists(os.path.abspath(dir_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid path in create_dir file ajax call ({dir_path})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid path in create_dir file ajax call ({dir_path})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
# Create the directory
|
# Create the directory
|
||||||
os.mkdir(dir_path)
|
os.mkdir(dir_path)
|
||||||
|
|
||||||
elif page == "unzip_file":
|
elif page == "unzip_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
path = helper.get_os_understandable_path(self.get_argument('path', None))
|
path = Helpers.get_os_understandable_path(self.get_argument("path", None))
|
||||||
helper.unzipFile(path)
|
Helpers.unzip_file(path)
|
||||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def delete(self, page):
|
def delete(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
|
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
if page == "del_file":
|
if page == "del_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
file_path = Helpers.get_os_understandable_path(
|
||||||
|
self.get_body_argument("file_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
|
||||||
console.warning(f"Delete {file_path} for server {server_id}")
|
Console.warning(f"Delete {file_path} for server {server_id}")
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'del_file'):
|
if not self.check_server_id(server_id, "del_file"):
|
||||||
return
|
|
||||||
else: server_id = bleach.clean(server_id)
|
|
||||||
|
|
||||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
|
||||||
if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \
|
|
||||||
or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \
|
|
||||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
|
||||||
logger.warning(f"Invalid path in del_file file ajax call ({file_path})")
|
|
||||||
console.warning(f"Invalid path in del_file file ajax call ({file_path})")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Delete the file
|
|
||||||
file_helper.del_file(file_path)
|
|
||||||
|
|
||||||
elif page == "del_dir":
|
|
||||||
if not permissions['Files'] in user_perms:
|
|
||||||
if not superuser:
|
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
|
||||||
return
|
|
||||||
dir_path = helper.get_os_understandable_path(self.get_body_argument('dir_path', default=None, strip=True))
|
|
||||||
|
|
||||||
console.warning(f"Delete {dir_path} for server {server_id}")
|
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'del_dir'):
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
if not helper.in_path(helper.get_os_understandable_path(server_info['path']), dir_path) \
|
if not (
|
||||||
or not helper.check_path_exists(os.path.abspath(dir_path)):
|
Helpers.in_path(
|
||||||
|
Helpers.get_os_understandable_path(server_info["path"]), file_path
|
||||||
|
)
|
||||||
|
or Helpers.in_path(
|
||||||
|
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||||
|
file_path,
|
||||||
|
)
|
||||||
|
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||||
|
logger.warning(f"Invalid path in del_file file ajax call ({file_path})")
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid path in del_file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Delete the file
|
||||||
|
FileHelpers.del_file(file_path)
|
||||||
|
|
||||||
|
elif page == "del_dir":
|
||||||
|
if not permissions["Files"] in user_perms:
|
||||||
|
if not superuser:
|
||||||
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
|
return
|
||||||
|
dir_path = Helpers.get_os_understandable_path(
|
||||||
|
self.get_body_argument("dir_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
Console.warning(f"Delete {dir_path} for server {server_id}")
|
||||||
|
|
||||||
|
if not self.check_server_id(server_id, "del_dir"):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
|
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
|
if not Helpers.in_path(
|
||||||
|
Helpers.get_os_understandable_path(server_info["path"]), dir_path
|
||||||
|
) or not Helpers.check_path_exists(os.path.abspath(dir_path)):
|
||||||
logger.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
logger.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
||||||
console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
Console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Delete the directory
|
# Delete the directory
|
||||||
# os.rmdir(dir_path) # Would only remove empty directories
|
# os.rmdir(dir_path) # Would only remove empty directories
|
||||||
if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), dir_path):
|
if Helpers.validate_traversal(
|
||||||
|
Helpers.get_os_understandable_path(server_info["path"]), dir_path
|
||||||
|
):
|
||||||
# Removes also when there are contents
|
# Removes also when there are contents
|
||||||
file_helper.del_dirs(dir_path)
|
FileHelpers.del_dirs(dir_path)
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def put(self, page):
|
def put(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
if page == "save_file":
|
if page == "save_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
file_contents = self.get_body_argument('file_contents', default=None, strip=True)
|
file_contents = self.get_body_argument(
|
||||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
"file_contents", default=None, strip=True
|
||||||
|
)
|
||||||
|
file_path = Helpers.get_os_understandable_path(
|
||||||
|
self.get_body_argument("file_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'save_file'):
|
if not self.check_server_id(server_id, "save_file"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\
|
if not Helpers.in_path(
|
||||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid path in save_file file ajax call ({file_path})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid path in save_file file ajax call ({file_path})")
|
),
|
||||||
|
file_path,
|
||||||
|
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid path in save_file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid path in save_file file ajax call ({file_path})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Open the file in write mode and store the content in file_object
|
# Open the file in write mode and store the content in file_object
|
||||||
with open(file_path, 'w', encoding='utf-8') as file_object:
|
with open(file_path, "w", encoding="utf-8") as file_object:
|
||||||
file_object.write(file_contents)
|
file_object.write(file_contents)
|
||||||
|
|
||||||
elif page == "rename_file":
|
elif page == "rename_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True))
|
item_path = Helpers.get_os_understandable_path(
|
||||||
new_item_name = self.get_body_argument('new_item_name', default=None, strip=True)
|
self.get_body_argument("item_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
new_item_name = self.get_body_argument(
|
||||||
|
"new_item_name", default=None, strip=True
|
||||||
|
)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'rename_file'):
|
if not self.check_server_id(server_id, "rename_file"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if item_path is None or new_item_name is None:
|
if item_path is None or new_item_name is None:
|
||||||
logger.warning("Invalid path(s) in rename_file file ajax call")
|
logger.warning("Invalid path(s) in rename_file file ajax call")
|
||||||
console.warning("Invalid path(s) in rename_file file ajax call")
|
Console.warning("Invalid path(s) in rename_file file ajax call")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \
|
if not Helpers.in_path(
|
||||||
or not helper.check_path_exists(os.path.abspath(item_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})")
|
),
|
||||||
|
item_path,
|
||||||
|
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']),
|
if not Helpers.in_path(
|
||||||
new_item_path) \
|
Helpers.get_os_understandable_path(
|
||||||
or helper.check_path_exists(os.path.abspath(new_item_path)):
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})")
|
),
|
||||||
console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})")
|
new_item_path,
|
||||||
|
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# RENAME
|
# RENAME
|
||||||
os.rename(item_path, new_item_path)
|
os.rename(item_path, new_item_path)
|
||||||
|
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def patch(self, page):
|
def patch(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
superuser = exec_user['superuser']
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument("id", None)
|
||||||
permissions = {
|
permissions = {
|
||||||
'Commands': Enum_Permissions_Server.Commands,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
'Terminal': Enum_Permissions_Server.Terminal,
|
"Terminal": EnumPermissionsServer.TERMINAL,
|
||||||
'Logs': Enum_Permissions_Server.Logs,
|
"Logs": EnumPermissionsServer.LOGS,
|
||||||
'Schedule': Enum_Permissions_Server.Schedule,
|
"Schedule": EnumPermissionsServer.SCHEDULE,
|
||||||
'Backup': Enum_Permissions_Server.Backup,
|
"Backup": EnumPermissionsServer.BACKUP,
|
||||||
'Files': Enum_Permissions_Server.Files,
|
"Files": EnumPermissionsServer.FILES,
|
||||||
'Config': Enum_Permissions_Server.Config,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
'Players': Enum_Permissions_Server.Players,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
user_perms = self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
if page == "rename_file":
|
if page == "rename_file":
|
||||||
if not permissions['Files'] in user_perms:
|
if not permissions["Files"] in user_perms:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||||
return
|
return
|
||||||
item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True))
|
item_path = Helpers.get_os_understandable_path(
|
||||||
new_item_name = self.get_body_argument('new_item_name', default=None, strip=True)
|
self.get_body_argument("item_path", default=None, strip=True)
|
||||||
|
)
|
||||||
|
new_item_name = self.get_body_argument(
|
||||||
|
"new_item_name", default=None, strip=True
|
||||||
|
)
|
||||||
|
|
||||||
if not self.check_server_id(server_id, 'rename_file'):
|
if not self.check_server_id(server_id, "rename_file"):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
if item_path is None or new_item_name is None:
|
if item_path is None or new_item_name is None:
|
||||||
logger.warning("Invalid path(s) in rename_file file ajax call")
|
logger.warning("Invalid path(s) in rename_file file ajax call")
|
||||||
console.warning("Invalid path(s) in rename_file file ajax call")
|
Console.warning("Invalid path(s) in rename_file file ajax call")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \
|
if not Helpers.in_path(
|
||||||
or not helper.check_path_exists(os.path.abspath(item_path)):
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})")
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})")
|
),
|
||||||
|
item_path,
|
||||||
|
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']),
|
if not Helpers.in_path(
|
||||||
new_item_path) \
|
Helpers.get_os_understandable_path(
|
||||||
or helper.check_path_exists(os.path.abspath(new_item_path)):
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})")
|
),
|
||||||
console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})")
|
new_item_path,
|
||||||
|
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
|
||||||
|
logger.warning(
|
||||||
|
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# RENAME
|
# RENAME
|
||||||
@ -400,15 +505,23 @@ class FileHandler(BaseHandler):
|
|||||||
|
|
||||||
def check_server_id(self, server_id, page_name):
|
def check_server_id(self, server_id, page_name):
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})")
|
logger.warning(
|
||||||
console.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})")
|
f"Server ID not defined in {page_name} file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Server ID not defined in {page_name} file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_id = bleach.clean(server_id)
|
server_id = bleach.clean(server_id)
|
||||||
|
|
||||||
# does this server id exist?
|
# does this server id exist?
|
||||||
if not self.controller.servers.server_id_exists(server_id):
|
if not self.controller.servers.server_id_exists(server_id):
|
||||||
logger.warning(f"Server ID not found in {page_name} file ajax call ({server_id})")
|
logger.warning(
|
||||||
console.warning(f"Server ID not found in {page_name} file ajax call ({server_id})")
|
f"Server ID not found in {page_name} file ajax call ({server_id})"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"Server ID not found in {page_name} file ajax call ({server_id})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
return True
|
return True
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HTTPHandler(BaseHandler):
|
class HTTPHandler(BaseHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
url = str(self.request.host)
|
url = str(self.request.host)
|
||||||
port = 443
|
port = 443
|
||||||
url_list = url.split(":")
|
url_list = url.split(":")
|
||||||
if url_list[0] != "":
|
if url_list[0] != "":
|
||||||
url = 'https://' + url_list[0]
|
url = "https://" + url_list[0]
|
||||||
else:
|
else:
|
||||||
url = 'https://' + url
|
url = "https://" + url
|
||||||
db_port = helper.get_setting('https_port')
|
db_port = self.helper.get_setting("https_port")
|
||||||
try:
|
try:
|
||||||
resp = requests.get(url + ":" + str(port))
|
resp = requests.get(url + ":" + str(port))
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
except Exception:
|
except Exception:
|
||||||
port = db_port
|
port = db_port
|
||||||
self.redirect(url+":"+str(port))
|
self.redirect(url + ":" + str(port))
|
||||||
|
|
||||||
|
|
||||||
class HTTPHandlerPage(BaseHandler):
|
class HTTPHandlerPage(BaseHandler):
|
||||||
@ -35,13 +30,13 @@ class HTTPHandlerPage(BaseHandler):
|
|||||||
port = 443
|
port = 443
|
||||||
url_list = url.split(":")
|
url_list = url.split(":")
|
||||||
if url_list[0] != "":
|
if url_list[0] != "":
|
||||||
url = 'https://' + url_list[0]
|
url = "https://" + url_list[0]
|
||||||
else:
|
else:
|
||||||
url = 'https://' + url
|
url = "https://" + url
|
||||||
db_port = helper.get_setting('https_port')
|
db_port = self.helper.get_setting("https_port")
|
||||||
try:
|
try:
|
||||||
resp = requests.get(url + ":" + str(port))
|
resp = requests.get(url + ":" + str(port))
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
except Exception:
|
except Exception:
|
||||||
port = db_port
|
port = db_port
|
||||||
self.redirect(url+":"+str(port))
|
self.redirect(url + ":" + str(port))
|
||||||
|
@ -1,26 +1,28 @@
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HTTPHandlerPage(BaseHandler):
|
class HTTPHandlerPage(BaseHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
url = self.request.full_url
|
url = self.request.full_url
|
||||||
port = 443
|
port = 443
|
||||||
if url[len(url)-1] == '/':
|
if url[len(url) - 1] == "/":
|
||||||
url = url.strip(url[len(url)-1])
|
url = url.strip(url[len(url) - 1])
|
||||||
url_list = url.split('/')
|
url_list = url.split("/")
|
||||||
if url_list[0] != "":
|
if url_list[0] != "":
|
||||||
primary_url = url_list[0] + ":"+str(port)+"/"
|
primary_url = url_list[0] + ":" + str(port) + "/"
|
||||||
backup_url = url_list[0] + ":" +str(helper.get_setting("https_port")) +"/"
|
backup_url = (
|
||||||
for i in range(len(url_list)-1):
|
url_list[0] + ":" + str(self.helper.get_setting("https_port")) + "/"
|
||||||
primary_url += url_list[i+1]
|
)
|
||||||
backup_url += url_list[i+1]
|
for i in range(len(url_list) - 1):
|
||||||
|
primary_url += url_list[i + 1]
|
||||||
|
backup_url += url_list[i + 1]
|
||||||
else:
|
else:
|
||||||
primary_url = url + str(port)
|
primary_url = url + str(port)
|
||||||
backup_url = url + str(helper.get_setting('https_port'))
|
backup_url = url + str(self.helper.get_setting("https_port"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = requests.get(primary_url)
|
resp = requests.get(primary_url)
|
||||||
@ -28,4 +30,4 @@ class HTTPHandlerPage(BaseHandler):
|
|||||||
url = primary_url
|
url = primary_url
|
||||||
except Exception:
|
except Exception:
|
||||||
url = backup_url
|
url = backup_url
|
||||||
self.redirect('https://'+url+':'+ str(port))
|
self.redirect("https://" + url + ":" + str(port))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,44 +1,51 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import bleach
|
||||||
|
|
||||||
from app.classes.models.users import Users
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.authentication import authentication
|
from app.classes.models.users import HelperUsers
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.shared.main_models import fn
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import bleach
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class PublicHandler(BaseHandler):
|
|
||||||
|
|
||||||
|
class PublicHandler(BaseHandler):
|
||||||
def set_current_user(self, user_id: str = None):
|
def set_current_user(self, user_id: str = None):
|
||||||
|
|
||||||
expire_days = helper.get_setting('cookie_expire')
|
expire_days = self.helper.get_setting("cookie_expire")
|
||||||
|
|
||||||
# if helper comes back with false
|
# if helper comes back with false
|
||||||
if not expire_days:
|
if not expire_days:
|
||||||
expire_days = "5"
|
expire_days = "5"
|
||||||
|
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
self.set_cookie("token", authentication.generate(user_id), expires_days=int(expire_days))
|
self.set_cookie(
|
||||||
|
"token",
|
||||||
|
self.controller.authentication.generate(user_id),
|
||||||
|
expires_days=int(expire_days),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.clear_cookie("user")
|
self.clear_cookie("token")
|
||||||
|
# self.clear_cookie("user")
|
||||||
|
# self.clear_cookie("user_data")
|
||||||
|
|
||||||
def get(self, page=None):
|
def get(self, page=None):
|
||||||
|
|
||||||
error = bleach.clean(self.get_argument('error', "Invalid Login!"))
|
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||||
error_msg = bleach.clean(self.get_argument('error_msg', ''))
|
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||||
|
try:
|
||||||
page_data = {
|
page_data = {
|
||||||
'version': helper.get_version_string(),
|
"version": self.helper.get_version_string(),
|
||||||
'error': error, 'lang': helper.get_setting('language'),
|
"error": error,
|
||||||
'lang_page': helper.getLangPage(helper.get_setting('language'))
|
"lang": self.helper.get_setting("language"),
|
||||||
}
|
"lang_page": self.helper.get_lang_page(
|
||||||
|
self.helper.get_setting("language")
|
||||||
|
),
|
||||||
|
"query": "",
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
self.redirect("/public/login.html")
|
||||||
|
if self.request.query:
|
||||||
|
page_data["query"] = self.request.query
|
||||||
|
|
||||||
# sensible defaults
|
# sensible defaults
|
||||||
template = "public/404.html"
|
template = "public/404.html"
|
||||||
@ -53,75 +60,141 @@ class PublicHandler(BaseHandler):
|
|||||||
template = "public/error.html"
|
template = "public/error.html"
|
||||||
|
|
||||||
elif page == "logout":
|
elif page == "logout":
|
||||||
self.clear_cookie("user")
|
self.clear_cookie("token")
|
||||||
self.clear_cookie("user_data")
|
# self.clear_cookie("user")
|
||||||
self.redirect('/public/login')
|
# self.clear_cookie("user_data")
|
||||||
|
self.redirect("/public/login")
|
||||||
return
|
return
|
||||||
|
|
||||||
# if we have no page, let's go to login
|
# if we have no page, let's go to login
|
||||||
else:
|
else:
|
||||||
self.redirect('/public/login')
|
if self.request.query:
|
||||||
|
self.redirect("/public/login?" + self.request.query)
|
||||||
|
else:
|
||||||
|
self.redirect("/public/login")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
data=page_data,
|
data=page_data,
|
||||||
translate=self.translator.translate,
|
translate=self.translator.translate,
|
||||||
error_msg = error_msg
|
error_msg=error_msg,
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, page=None):
|
def post(self, page=None):
|
||||||
|
|
||||||
if page == 'login':
|
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||||
next_page = "/public/login"
|
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||||
|
|
||||||
entered_username = bleach.clean(self.get_argument('username'))
|
page_data = {
|
||||||
entered_password = bleach.clean(self.get_argument('password'))
|
"version": self.helper.get_version_string(),
|
||||||
|
"error": error,
|
||||||
|
"lang": self.helper.get_setting("language"),
|
||||||
|
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
||||||
|
"query": "",
|
||||||
|
}
|
||||||
|
if self.request.query:
|
||||||
|
page_data["query"] = self.request.query
|
||||||
|
|
||||||
|
if page == "login":
|
||||||
|
|
||||||
|
next_page = "/public/login"
|
||||||
|
if self.request.query:
|
||||||
|
next_page = "/public/login?" + self.request.query
|
||||||
|
|
||||||
|
entered_username = bleach.clean(self.get_argument("username"))
|
||||||
|
entered_password = bleach.clean(self.get_argument("password"))
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
user_data = Users.get_or_none(fn.Lower(Users.username) == entered_username.lower())
|
try:
|
||||||
|
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
|
||||||
|
user_data = HelperUsers.get_user_model(user_id)
|
||||||
|
except:
|
||||||
|
error_msg = "Incorrect username or password. Please try again."
|
||||||
|
# self.clear_cookie("user")
|
||||||
|
# self.clear_cookie("user_data")
|
||||||
|
self.clear_cookie("token")
|
||||||
|
if self.request.query:
|
||||||
|
self.redirect(
|
||||||
|
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.redirect(f"/public/login?error_msg={error_msg}")
|
||||||
|
return
|
||||||
|
|
||||||
# if we don't have a user
|
# if we don't have a user
|
||||||
if not user_data:
|
if not user_data:
|
||||||
error_msg = "Incorrect username or password. Please try again."
|
error_msg = "Incorrect username or password. Please try again."
|
||||||
self.clear_cookie("user")
|
# self.clear_cookie("user")
|
||||||
self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.redirect(f'/public/login?error_msg={error_msg}')
|
self.clear_cookie("token")
|
||||||
|
if self.request.query:
|
||||||
|
self.redirect(
|
||||||
|
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.redirect(f"/public/login?error_msg={error_msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# if they are disabled
|
# if they are disabled
|
||||||
if not user_data.enabled:
|
if not user_data.enabled:
|
||||||
error_msg = "User account disabled. Please contact your system administrator for more info."
|
error_msg = (
|
||||||
self.clear_cookie("user")
|
"User account disabled. Please contact "
|
||||||
self.clear_cookie("user_data")
|
"your system administrator for more info."
|
||||||
self.redirect(f'/public/login?error_msg={error_msg}')
|
)
|
||||||
|
# self.clear_cookie("user")
|
||||||
|
# self.clear_cookie("user_data")
|
||||||
|
self.clear_cookie("token")
|
||||||
|
if self.request.query:
|
||||||
|
self.redirect(
|
||||||
|
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.redirect(f"/public/login?error_msg={error_msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
login_result = helper.verify_pass(entered_password, user_data.password)
|
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
||||||
|
|
||||||
# Valid Login
|
# Valid Login
|
||||||
if login_result:
|
if login_result:
|
||||||
self.set_current_user(user_data.user_id)
|
self.set_current_user(user_data.user_id)
|
||||||
logger.info(f"User: {user_data} Logged in from IP: {self.get_remote_ip()}")
|
logger.info(
|
||||||
|
f"User: {user_data} Logged in from IP: {self.get_remote_ip()}"
|
||||||
|
)
|
||||||
|
|
||||||
# record this login
|
# record this login
|
||||||
q = Users.select().where(Users.username == entered_username.lower()).get()
|
user_data.last_ip = self.get_remote_ip()
|
||||||
q.last_ip = self.get_remote_ip()
|
user_data.last_login = Helpers.get_time_as_string()
|
||||||
q.last_login = helper.get_time_as_string()
|
user_data.save()
|
||||||
q.save()
|
|
||||||
|
|
||||||
# log this login
|
# log this login
|
||||||
self.controller.management.add_to_audit_log(user_data.user_id, "Logged in", 0, self.get_remote_ip())
|
self.controller.management.add_to_audit_log(
|
||||||
|
user_data.user_id, "Logged in", 0, self.get_remote_ip()
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.request.query_arguments.get("next"):
|
||||||
|
next_page = self.request.query_arguments.get("next")[0].decode()
|
||||||
|
else:
|
||||||
|
next_page = "/panel/dashboard"
|
||||||
|
|
||||||
next_page = "/panel/dashboard"
|
|
||||||
self.redirect(next_page)
|
self.redirect(next_page)
|
||||||
else:
|
else:
|
||||||
self.clear_cookie("user")
|
# self.clear_cookie("user")
|
||||||
self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
|
self.clear_cookie("token")
|
||||||
error_msg = "Inncorrect username or password. Please try again."
|
error_msg = "Inncorrect username or password. Please try again."
|
||||||
# log this failed login attempt
|
# log this failed login attempt
|
||||||
self.controller.management.add_to_audit_log(user_data.user_id, "Tried to log in", 0, self.get_remote_ip())
|
self.controller.management.add_to_audit_log(
|
||||||
self.redirect(f'/public/login?error_msg={error_msg}')
|
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||||
|
)
|
||||||
|
if self.request.query:
|
||||||
|
self.redirect(
|
||||||
|
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.redirect(f"/public/login?error_msg={error_msg}")
|
||||||
else:
|
else:
|
||||||
self.redirect("/public/login")
|
if self.request.query:
|
||||||
|
self.redirect("/public/login?" + self.request.query)
|
||||||
|
else:
|
||||||
|
self.redirect("/public/login")
|
||||||
|
157
app/classes/web/routes/api/api_handlers.py
Normal file
157
app/classes/web/routes/api/api_handlers.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
from app.classes.web.routes.api.index_handler import ApiIndexHandler
|
||||||
|
from app.classes.web.routes.api.jsonschema import (
|
||||||
|
ApiJsonSchemaHandler,
|
||||||
|
ApiJsonSchemaListHandler,
|
||||||
|
)
|
||||||
|
from app.classes.web.routes.api.not_found import ApiNotFoundHandler
|
||||||
|
from app.classes.web.routes.api.auth.invalidate_tokens import (
|
||||||
|
ApiAuthInvalidateTokensHandler,
|
||||||
|
)
|
||||||
|
from app.classes.web.routes.api.auth.login import ApiAuthLoginHandler
|
||||||
|
from app.classes.web.routes.api.roles.index import ApiRolesIndexHandler
|
||||||
|
from app.classes.web.routes.api.roles.role.index import ApiRolesRoleIndexHandler
|
||||||
|
from app.classes.web.routes.api.roles.role.servers import ApiRolesRoleServersHandler
|
||||||
|
from app.classes.web.routes.api.roles.role.users import ApiRolesRoleUsersHandler
|
||||||
|
from app.classes.web.routes.api.servers.index import ApiServersIndexHandler
|
||||||
|
from app.classes.web.routes.api.servers.server.action import (
|
||||||
|
ApiServersServerActionHandler,
|
||||||
|
)
|
||||||
|
from app.classes.web.routes.api.servers.server.index import ApiServersServerIndexHandler
|
||||||
|
from app.classes.web.routes.api.servers.server.logs import ApiServersServerLogsHandler
|
||||||
|
from app.classes.web.routes.api.servers.server.public import (
|
||||||
|
ApiServersServerPublicHandler,
|
||||||
|
)
|
||||||
|
from app.classes.web.routes.api.servers.server.stats import ApiServersServerStatsHandler
|
||||||
|
from app.classes.web.routes.api.servers.server.users import ApiServersServerUsersHandler
|
||||||
|
from app.classes.web.routes.api.users.index import ApiUsersIndexHandler
|
||||||
|
from app.classes.web.routes.api.users.user.index import ApiUsersUserIndexHandler
|
||||||
|
from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler
|
||||||
|
from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler
|
||||||
|
|
||||||
|
|
||||||
|
def api_handlers(handler_args):
|
||||||
|
return [
|
||||||
|
# Auth routes
|
||||||
|
(
|
||||||
|
r"/api/v2/auth/login/?",
|
||||||
|
ApiAuthLoginHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/auth/invalidate_tokens/?",
|
||||||
|
ApiAuthInvalidateTokensHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
# User routes
|
||||||
|
(
|
||||||
|
r"/api/v2/users/?",
|
||||||
|
ApiUsersIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/([0-9]+)/?",
|
||||||
|
ApiUsersUserIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/(@me)/?",
|
||||||
|
ApiUsersUserIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/([0-9]+)/pfp/?",
|
||||||
|
ApiUsersUserPfpHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/(@me)/pfp/?",
|
||||||
|
ApiUsersUserPfpHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/([0-9]+)/public/?",
|
||||||
|
ApiUsersUserPublicHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/users/(@me)/public/?",
|
||||||
|
ApiUsersUserPublicHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
# Server routes
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/?",
|
||||||
|
ApiServersIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/?",
|
||||||
|
ApiServersServerIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/stats/?",
|
||||||
|
ApiServersServerStatsHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/action/([a-z_]+)/?",
|
||||||
|
ApiServersServerActionHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/logs/?",
|
||||||
|
ApiServersServerLogsHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/users/?",
|
||||||
|
ApiServersServerUsersHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/([0-9]+)/public/?",
|
||||||
|
ApiServersServerPublicHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/roles/?",
|
||||||
|
ApiRolesIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/roles/([0-9]+)/?",
|
||||||
|
ApiRolesRoleIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/roles/([0-9]+)/servers/?",
|
||||||
|
ApiRolesRoleServersHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/roles/([0-9]+)/users/?",
|
||||||
|
ApiRolesRoleUsersHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/jsonschema/?",
|
||||||
|
ApiJsonSchemaListHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/jsonschema/([a-z0-9_]+)/?",
|
||||||
|
ApiJsonSchemaHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/?",
|
||||||
|
ApiIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/(.*)",
|
||||||
|
ApiNotFoundHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
]
|
21
app/classes/web/routes/api/auth/invalidate_tokens.py
Normal file
21
app/classes/web/routes/api/auth/invalidate_tokens.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
from app.classes.shared.console import Console
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiAuthInvalidateTokensHandler(BaseApiHandler):
|
||||||
|
def post(self):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: Invalidate tokens
|
||||||
|
Console.info("invalidate_tokens")
|
||||||
|
self.controller.users.raw_update_user(
|
||||||
|
auth_data[4]["user_id"], {"valid_tokens_from": datetime.datetime.now()}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(200, {"status": "ok"})
|
104
app/classes/web/routes/api/auth/login.py
Normal file
104
app/classes/web/routes/api/auth/login.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from jsonschema import validate
|
||||||
|
from jsonschema.exceptions import ValidationError
|
||||||
|
from app.classes.models.users import Users
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
login_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 20,
|
||||||
|
"minLength": 4,
|
||||||
|
"pattern": "^[a-z0-9_]+$",
|
||||||
|
},
|
||||||
|
"password": {"type": "string", "maxLength": 20, "minLength": 4},
|
||||||
|
},
|
||||||
|
"required": ["username", "password"],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiAuthLoginHandler(BaseApiHandler):
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(self.request.body)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, login_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
username = data["username"]
|
||||||
|
password = data["password"]
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
user_data = Users.get_or_none(Users.username == username)
|
||||||
|
|
||||||
|
if user_data is None:
|
||||||
|
return self.finish_json(
|
||||||
|
401,
|
||||||
|
{"status": "error", "error": "INCORRECT_CREDENTIALS", "token": None},
|
||||||
|
)
|
||||||
|
|
||||||
|
if not user_data.enabled:
|
||||||
|
self.finish_json(
|
||||||
|
403, {"status": "error", "error": "ACCOUNT_DISABLED", "token": None}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
login_result = self.helper.verify_pass(password, user_data.password)
|
||||||
|
|
||||||
|
# Valid Login
|
||||||
|
if login_result:
|
||||||
|
logger.info(f"User: {user_data} Logged in from IP: {self.get_remote_ip()}")
|
||||||
|
|
||||||
|
# record this login
|
||||||
|
query = Users.select().where(Users.username == username.lower()).get()
|
||||||
|
query.last_ip = self.get_remote_ip()
|
||||||
|
query.last_login = Helpers.get_time_as_string()
|
||||||
|
query.save()
|
||||||
|
|
||||||
|
# log this login
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user_data.user_id, "logged in via the API", 0, self.get_remote_ip()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
"token": self.controller.authentication.generate(
|
||||||
|
user_data.user_id
|
||||||
|
),
|
||||||
|
"user_id": str(user_data.user_id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# log this failed login attempt
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||||
|
)
|
||||||
|
self.finish_json(
|
||||||
|
401,
|
||||||
|
{"status": "error", "error": "INCORRECT_CREDENTIALS"},
|
||||||
|
)
|
2
app/classes/web/routes/api/auth/register.py
Normal file
2
app/classes/web/routes/api/auth/register.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# nothing here yet
|
||||||
|
# sometime implement configurable self service account creation?
|
17
app/classes/web/routes/api/index_handler.py
Normal file
17
app/classes/web/routes/api/index_handler.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
WIKI_API_LINK = "https://wiki.craftycontrol.com/en/4/docs/API V2"
|
||||||
|
|
||||||
|
|
||||||
|
class ApiIndexHandler(BaseApiHandler):
|
||||||
|
def get(self):
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
"version": self.controller.helper.get_version_string(),
|
||||||
|
"message": f"Please see the API documentation at {WIKI_API_LINK}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
107
app/classes/web/routes/api/jsonschema.py
Normal file
107
app/classes/web/routes/api/jsonschema.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import typing as t
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
from app.classes.web.routes.api.auth.login import login_schema
|
||||||
|
from app.classes.web.routes.api.roles.role.index import modify_role_schema
|
||||||
|
from app.classes.web.routes.api.roles.index import create_role_schema
|
||||||
|
from app.classes.web.routes.api.servers.server.index import server_patch_schema
|
||||||
|
from app.classes.web.routes.api.servers.index import new_server_schema
|
||||||
|
|
||||||
|
SCHEMA_LIST: t.Final = [
|
||||||
|
"login",
|
||||||
|
"modify_role",
|
||||||
|
"create_role",
|
||||||
|
"server_patch",
|
||||||
|
"new_server",
|
||||||
|
"user_patch",
|
||||||
|
"new_user",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ApiJsonSchemaListHandler(BaseApiHandler):
|
||||||
|
def get(self):
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": SCHEMA_LIST},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiJsonSchemaHandler(BaseApiHandler):
|
||||||
|
def get(self, schema_name: str):
|
||||||
|
if schema_name == "login":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": login_schema},
|
||||||
|
)
|
||||||
|
elif schema_name == "modify_role":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": modify_role_schema},
|
||||||
|
)
|
||||||
|
elif schema_name == "create_role":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": create_role_schema},
|
||||||
|
)
|
||||||
|
elif schema_name == "server_patch":
|
||||||
|
self.finish_json(200, server_patch_schema)
|
||||||
|
elif schema_name == "new_server":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
new_server_schema,
|
||||||
|
)
|
||||||
|
elif schema_name == "user_patch":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
**self.controller.users.user_jsonschema_props,
|
||||||
|
},
|
||||||
|
"anyOf": [
|
||||||
|
# Require at least one property
|
||||||
|
{"required": [name]}
|
||||||
|
for name in [
|
||||||
|
"username",
|
||||||
|
"password",
|
||||||
|
"email",
|
||||||
|
"enabled",
|
||||||
|
"lang",
|
||||||
|
"superuser",
|
||||||
|
"permissions",
|
||||||
|
"roles",
|
||||||
|
"hints",
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"additionalProperties": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
elif schema_name == "new_user":
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
**self.controller.users.user_jsonschema_props,
|
||||||
|
},
|
||||||
|
"required": ["username", "password"],
|
||||||
|
"additionalProperties": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.finish_json(
|
||||||
|
404,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "UNKNOWN_JSON_SCHEMA",
|
||||||
|
"info": (
|
||||||
|
f"Unknown JSON schema: {schema_name}."
|
||||||
|
f" Here's a list of all the valid schema names: {SCHEMA_LIST}"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
18
app/classes/web/routes/api/not_found.py
Normal file
18
app/classes/web/routes/api/not_found.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from typing import Awaitable, Callable, Optional
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
class ApiNotFoundHandler(BaseApiHandler):
|
||||||
|
def _not_found(self, page: str) -> None:
|
||||||
|
self.finish_json(
|
||||||
|
404,
|
||||||
|
{"status": "error", "error": "API_HANDLER_NOT_FOUND", "page": page},
|
||||||
|
)
|
||||||
|
|
||||||
|
head = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
get = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
post = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
delete = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
patch = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
put = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
||||||
|
options = _not_found # type: Callable[..., Optional[Awaitable[None]]]
|
131
app/classes/web/routes/api/roles/index.py
Normal file
131
app/classes/web/routes/api/roles/index.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import typing as t
|
||||||
|
from jsonschema import ValidationError, validate
|
||||||
|
import orjson
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
create_role_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"server_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[01]{8}$", # 8 bits, see EnumPermissionsServer
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["server_id", "permissions"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiRolesIndexHandler(BaseApiHandler):
|
||||||
|
def get(self):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
_,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
# GET /api/v2/roles?ids=true
|
||||||
|
get_only_ids = self.get_query_argument("ids", None) == "true"
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": self.controller.roles.get_all_role_ids()
|
||||||
|
if get_only_ids
|
||||||
|
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||||
|
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, create_role_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
role_name = data["name"]
|
||||||
|
|
||||||
|
# Get the servers
|
||||||
|
servers_dict = {server["server_id"]: server for server in data["servers"]}
|
||||||
|
server_ids = (
|
||||||
|
(
|
||||||
|
{server["server_id"] for server in data["servers"]}
|
||||||
|
& set(self.controller.get_all_server_ids())
|
||||||
|
) # Only allow existing servers
|
||||||
|
if "servers" in data
|
||||||
|
else set()
|
||||||
|
)
|
||||||
|
servers: t.List[dict] = [servers_dict[server_id] for server_id in server_ids]
|
||||||
|
|
||||||
|
if self.controller.roles.get_roleid_by_name(role_name) is not None:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "ROLE_NAME_ALREADY_EXISTS"}
|
||||||
|
)
|
||||||
|
|
||||||
|
role_id = self.controller.roles.add_role_advanced(role_name, servers)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
f"created role {role_name} (RID:{role_id})",
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": {"role_id": role_id}},
|
||||||
|
)
|
143
app/classes/web/routes/api/roles/role/index.py
Normal file
143
app/classes/web/routes/api/roles/role/index.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
from jsonschema import ValidationError, validate
|
||||||
|
import orjson
|
||||||
|
from peewee import DoesNotExist
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
modify_role_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"server_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[01]{8}$", # 8 bits, see EnumPermissionsServer
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["server_id", "permissions"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"anyOf": [
|
||||||
|
{"required": ["name"]},
|
||||||
|
{"required": ["servers"]},
|
||||||
|
],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiRolesRoleIndexHandler(BaseApiHandler):
|
||||||
|
def get(self, role_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
_,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": self.controller.roles.get_role(role_id)},
|
||||||
|
)
|
||||||
|
except DoesNotExist:
|
||||||
|
self.finish_json(404, {"status": "error", "error": "ROLE_NOT_FOUND"})
|
||||||
|
|
||||||
|
def delete(self, role_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
self.controller.roles.remove_role(role_id)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": role_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
f"deleted role with ID {role_id}",
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def patch(self, role_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||||
|
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, modify_role_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.controller.roles.update_role_advanced(
|
||||||
|
role_id, data.get("role_name", None), data.get("servers", None)
|
||||||
|
)
|
||||||
|
except DoesNotExist:
|
||||||
|
return self.finish_json(404, {"status": "error", "error": "ROLE_NOT_FOUND"})
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
f"modified role with ID {role_id}",
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok"},
|
||||||
|
)
|
32
app/classes/web/routes/api/roles/role/servers.py
Normal file
32
app/classes/web/routes/api/roles/role/servers.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
class ApiRolesRoleServersHandler(BaseApiHandler):
|
||||||
|
def get(self, role_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
_,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
# GET /api/v2/roles/role/servers?ids=true
|
||||||
|
get_only_ids = self.get_query_argument("ids", None) == "true"
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": PermissionsServers.get_server_ids_from_role(role_id)
|
||||||
|
if get_only_ids
|
||||||
|
else self.controller.roles.get_server_ids_and_perms_from_role(role_id),
|
||||||
|
},
|
||||||
|
)
|
36
app/classes/web/routes/api/roles/role/users.py
Normal file
36
app/classes/web/routes/api/roles/role/users.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
class ApiRolesRoleUsersHandler(BaseApiHandler):
|
||||||
|
def get(self, role_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
_,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if not superuser:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
all_user_ids = self.controller.users.get_all_user_ids()
|
||||||
|
|
||||||
|
user_roles = {}
|
||||||
|
for user_id in all_user_ids:
|
||||||
|
user_roles_list = self.controller.users.get_user_roles_names(user_id)
|
||||||
|
user_roles[user_id] = user_roles_list
|
||||||
|
|
||||||
|
role = self.controller.roles.get_role(role_id)
|
||||||
|
|
||||||
|
user_ids = []
|
||||||
|
|
||||||
|
for user_id in all_user_ids:
|
||||||
|
for role_user in user_roles[user_id]:
|
||||||
|
if role_user == role["role_name"]:
|
||||||
|
user_ids.append(user_id)
|
||||||
|
|
||||||
|
self.finish_json(200, {"status": "ok", "data": user_ids})
|
713
app/classes/web/routes/api/servers/index.py
Normal file
713
app/classes/web/routes/api/servers/index.py
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from jsonschema import ValidationError, validate
|
||||||
|
import orjson
|
||||||
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
new_server_schema = {
|
||||||
|
"definitions": {},
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Root",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"monitoring_type",
|
||||||
|
"create_type",
|
||||||
|
],
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"name": "My Server",
|
||||||
|
"monitoring_type": "minecraft_java",
|
||||||
|
"minecraft_java_monitoring_data": {"host": "127.0.0.1", "port": 25565},
|
||||||
|
"create_type": "minecraft_java",
|
||||||
|
"minecraft_java_create_data": {
|
||||||
|
"create_type": "download_jar",
|
||||||
|
"download_jar_create_data": {
|
||||||
|
"type": "Paper",
|
||||||
|
"version": "1.18.2",
|
||||||
|
"mem_min": 1,
|
||||||
|
"mem_max": 2,
|
||||||
|
"server_properties_port": 25565,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"title": "Name",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["My Server"],
|
||||||
|
"minLength": 2,
|
||||||
|
},
|
||||||
|
"stop_command": {
|
||||||
|
"title": "Stop command",
|
||||||
|
"description": '"" means the default for the server creation type.',
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"examples": ["stop", "end"],
|
||||||
|
},
|
||||||
|
"log_location": {
|
||||||
|
"title": "Log file",
|
||||||
|
"description": '"" means the default for the server creation type.',
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"examples": ["./logs/latest.log", "./proxy.log.0"],
|
||||||
|
},
|
||||||
|
"crashdetection": {
|
||||||
|
"title": "Crash detection",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
"autostart": {
|
||||||
|
"title": "Autostart",
|
||||||
|
"description": "If true, the server will be started"
|
||||||
|
+ " automatically when Crafty is launched.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
"autostart_delay": {
|
||||||
|
"title": "Autostart delay",
|
||||||
|
"description": "Delay in seconds before autostarting. (If enabled)",
|
||||||
|
"type": "number",
|
||||||
|
"default": 10,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
"monitoring_type": {
|
||||||
|
"title": "Server monitoring type",
|
||||||
|
"type": "string",
|
||||||
|
"default": "minecraft_java",
|
||||||
|
"enum": ["minecraft_java", "minecraft_bedrock", "none"],
|
||||||
|
# TODO: SteamCMD, RakNet, etc.
|
||||||
|
},
|
||||||
|
"minecraft_java_monitoring_data": {
|
||||||
|
"title": "Minecraft Java monitoring data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["host", "port"],
|
||||||
|
"properties": {
|
||||||
|
"host": {
|
||||||
|
"title": "Host",
|
||||||
|
"type": "string",
|
||||||
|
"default": "127.0.0.1",
|
||||||
|
"examples": ["127.0.0.1"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"title": "Port",
|
||||||
|
"type": "integer",
|
||||||
|
"examples": [25565],
|
||||||
|
"default": 25565,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"minecraft_bedrock_monitoring_data": {
|
||||||
|
"title": "Minecraft Bedrock monitoring data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["host", "port"],
|
||||||
|
"properties": {
|
||||||
|
"host": {
|
||||||
|
"title": "Host",
|
||||||
|
"type": "string",
|
||||||
|
"default": "127.0.0.1",
|
||||||
|
"examples": ["127.0.0.1"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"title": "Port",
|
||||||
|
"type": "integer",
|
||||||
|
"examples": [19132],
|
||||||
|
"default": 19132,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_type": {
|
||||||
|
# This is only used for creation, this is not saved in the db
|
||||||
|
"title": "Server creation type",
|
||||||
|
"type": "string",
|
||||||
|
"default": "minecraft_java",
|
||||||
|
"enum": ["minecraft_java", "minecraft_bedrock", "custom"],
|
||||||
|
},
|
||||||
|
"minecraft_java_create_data": {
|
||||||
|
"title": "Java creation data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["create_type"],
|
||||||
|
"properties": {
|
||||||
|
"create_type": {
|
||||||
|
"title": "Creation type",
|
||||||
|
"type": "string",
|
||||||
|
"default": "download_jar",
|
||||||
|
"enum": ["download_jar", "import_server", "import_zip"],
|
||||||
|
},
|
||||||
|
"download_jar_create_data": {
|
||||||
|
"title": "JAR download data",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"version",
|
||||||
|
"mem_min",
|
||||||
|
"mem_max",
|
||||||
|
"server_properties_port",
|
||||||
|
"agree_to_eula",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"title": "Server JAR Type",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["Paper"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"title": "Server JAR Version",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["1.18.2"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"mem_min": {
|
||||||
|
"title": "Minimum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [1],
|
||||||
|
"default": 1,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"mem_max": {
|
||||||
|
"title": "Maximum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [2],
|
||||||
|
"default": 2,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"server_properties_port": {
|
||||||
|
"title": "Port",
|
||||||
|
"type": "integer",
|
||||||
|
"examples": [25565],
|
||||||
|
"default": 25565,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
"agree_to_eula": {
|
||||||
|
"title": "Agree to the EULA",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"import_server_create_data": {
|
||||||
|
"title": "Import server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"existing_server_path",
|
||||||
|
"jarfile",
|
||||||
|
"mem_min",
|
||||||
|
"mem_max",
|
||||||
|
"server_properties_port",
|
||||||
|
"agree_to_eula",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"existing_server_path": {
|
||||||
|
"title": "Server path",
|
||||||
|
"description": "Absolute path to the old server",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"jarfile": {
|
||||||
|
"title": "JAR file",
|
||||||
|
"description": "The JAR file relative to the previous path",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["paper.jar", "jars/vanilla-1.12.jar"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"mem_min": {
|
||||||
|
"title": "Minimum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [1],
|
||||||
|
"default": 1,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"mem_max": {
|
||||||
|
"title": "Maximum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [2],
|
||||||
|
"default": 2,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"server_properties_port": {
|
||||||
|
"title": "Port",
|
||||||
|
"type": "integer",
|
||||||
|
"examples": [25565],
|
||||||
|
"default": 25565,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
"agree_to_eula": {
|
||||||
|
"title": "Agree to the EULA",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"import_zip_create_data": {
|
||||||
|
"title": "Import ZIP server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"zip_path",
|
||||||
|
"zip_root",
|
||||||
|
"jarfile",
|
||||||
|
"mem_min",
|
||||||
|
"mem_max",
|
||||||
|
"server_properties_port",
|
||||||
|
"agree_to_eula",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"zip_path": {
|
||||||
|
"title": "ZIP path",
|
||||||
|
"description": "Absolute path to the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server.zip"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"zip_root": {
|
||||||
|
"title": "Server root directory",
|
||||||
|
"description": "The server root in the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/", "/paper-server/", "server-1"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"jarfile": {
|
||||||
|
"title": "JAR file",
|
||||||
|
"description": "The JAR relative to the configured root",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["paper.jar", "jars/vanilla-1.12.jar"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"mem_min": {
|
||||||
|
"title": "Minimum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [1],
|
||||||
|
"default": 1,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"mem_max": {
|
||||||
|
"title": "Maximum JVM memory (in GiBs)",
|
||||||
|
"type": "number",
|
||||||
|
"examples": [2],
|
||||||
|
"default": 2,
|
||||||
|
"exclusiveMinimum": 0,
|
||||||
|
},
|
||||||
|
"server_properties_port": {
|
||||||
|
"title": "Port",
|
||||||
|
"type": "integer",
|
||||||
|
"examples": [25565],
|
||||||
|
"default": 25565,
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
"agree_to_eula": {
|
||||||
|
"title": "Agree to the EULA",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$comment": "If..then section",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "download_jar"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["download_jar_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "import_exec"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_server_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "import_zip"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_zip_create_data"]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Only one creation data",
|
||||||
|
"oneOf": [
|
||||||
|
{"required": ["download_jar_create_data"]},
|
||||||
|
{"required": ["import_server_create_data"]},
|
||||||
|
{"required": ["import_zip_create_data"]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"minecraft_bedrock_create_data": {
|
||||||
|
"title": "Minecraft Bedrock creation data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["create_type"],
|
||||||
|
"properties": {
|
||||||
|
"create_type": {
|
||||||
|
"title": "Creation type",
|
||||||
|
"type": "string",
|
||||||
|
"default": "import_server",
|
||||||
|
"enum": ["import_server", "import_zip"],
|
||||||
|
},
|
||||||
|
"import_server_create_data": {
|
||||||
|
"title": "Import server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["existing_server_path", "command"],
|
||||||
|
"properties": {
|
||||||
|
"existing_server_path": {
|
||||||
|
"title": "Server path",
|
||||||
|
"description": "Absolute path to the old server",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "Command",
|
||||||
|
"type": "string",
|
||||||
|
"default": "echo foo bar baz",
|
||||||
|
"examples": ["LD_LIBRARY_PATH=. ./bedrock_server"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"import_zip_create_data": {
|
||||||
|
"title": "Import ZIP server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["zip_path", "zip_root", "command"],
|
||||||
|
"properties": {
|
||||||
|
"zip_path": {
|
||||||
|
"title": "ZIP path",
|
||||||
|
"description": "Absolute path to the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server.zip"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"zip_root": {
|
||||||
|
"title": "Server root directory",
|
||||||
|
"description": "The server root in the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/", "/paper-server/", "server-1"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "Command",
|
||||||
|
"type": "string",
|
||||||
|
"default": "echo foo bar baz",
|
||||||
|
"examples": ["LD_LIBRARY_PATH=. ./bedrock_server"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$comment": "If..then section",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "import_exec"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_server_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "import_zip"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_zip_create_data"]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Only one creation data",
|
||||||
|
"oneOf": [
|
||||||
|
{"required": ["import_server_create_data"]},
|
||||||
|
{"required": ["import_zip_create_data"]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"custom_create_data": {
|
||||||
|
"title": "Custom creation data",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"working_directory",
|
||||||
|
"executable_update",
|
||||||
|
"create_type",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"working_directory": {
|
||||||
|
"title": "Working directory",
|
||||||
|
"description": '"" means the default',
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"examples": ["/mnt/mydrive/server-configs/", "./subdirectory", ""],
|
||||||
|
},
|
||||||
|
"executable_update": {
|
||||||
|
"title": "Executable Updation",
|
||||||
|
"description": "Also configurable later on and for other servers",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["enabled", "file", "url"],
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"title": "Enabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"title": "Executable to update",
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"examples": ["./paper.jar"],
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"title": "URL to download the executable from",
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_type": {
|
||||||
|
"title": "Creation type",
|
||||||
|
"type": "string",
|
||||||
|
"default": "raw_exec",
|
||||||
|
"enum": ["raw_exec", "import_server", "import_zip"],
|
||||||
|
},
|
||||||
|
"raw_exec_create_data": {
|
||||||
|
"title": "Raw execution command create data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["command"],
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"title": "Command",
|
||||||
|
"type": "string",
|
||||||
|
"default": "echo foo bar baz",
|
||||||
|
"examples": ["caddy start"],
|
||||||
|
"minLength": 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"import_server_create_data": {
|
||||||
|
"title": "Import server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["existing_server_path", "command"],
|
||||||
|
"properties": {
|
||||||
|
"existing_server_path": {
|
||||||
|
"title": "Server path",
|
||||||
|
"description": "Absolute path to the old server",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "Command",
|
||||||
|
"type": "string",
|
||||||
|
"default": "echo foo bar baz",
|
||||||
|
"examples": ["caddy start"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"import_zip_create_data": {
|
||||||
|
"title": "Import ZIP server data",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["zip_path", "zip_root", "command"],
|
||||||
|
"properties": {
|
||||||
|
"zip_path": {
|
||||||
|
"title": "ZIP path",
|
||||||
|
"description": "Absolute path to the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/var/opt/server.zip"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"zip_root": {
|
||||||
|
"title": "Server root directory",
|
||||||
|
"description": "The server root in the ZIP archive",
|
||||||
|
"type": "string",
|
||||||
|
"examples": ["/", "/paper-server/", "server-1"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "Command",
|
||||||
|
"type": "string",
|
||||||
|
"default": "echo foo bar baz",
|
||||||
|
"examples": ["caddy start"],
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$comment": "If..then section",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "raw_exec"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["raw_exec_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {
|
||||||
|
"create_type": {"const": "import_server"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_server_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "import_zip"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["import_zip_create_data"]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Only one creation data",
|
||||||
|
"oneOf": [
|
||||||
|
{"required": ["raw_exec_create_data"]},
|
||||||
|
{"required": ["import_server_create_data"]},
|
||||||
|
{"required": ["import_zip_create_data"]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$comment": "If..then section",
|
||||||
|
"allOf": [
|
||||||
|
# start require creation data
|
||||||
|
{
|
||||||
|
"if": {"properties": {"create_type": {"const": "minecraft_java"}}},
|
||||||
|
"then": {"required": ["minecraft_java_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"create_type": {"const": "minecraft_bedrock"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["minecraft_bedrock_create_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {"properties": {"create_type": {"const": "custom"}}},
|
||||||
|
"then": {"required": ["custom_create_data"]},
|
||||||
|
},
|
||||||
|
# end require creation data
|
||||||
|
# start require monitoring data
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {"monitoring_type": {"const": "minecraft_java"}}
|
||||||
|
},
|
||||||
|
"then": {"required": ["minecraft_java_monitoring_data"]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {
|
||||||
|
"monitoring_type": {"const": "minecraft_bedrock"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {"required": ["minecraft_bedrock_monitoring_data"]},
|
||||||
|
},
|
||||||
|
# end require monitoring data
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Only one creation data",
|
||||||
|
"oneOf": [
|
||||||
|
{"required": ["minecraft_java_create_data"]},
|
||||||
|
{"required": ["minecraft_bedrock_create_data"]},
|
||||||
|
{"required": ["custom_create_data"]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Only one monitoring data",
|
||||||
|
"oneOf": [
|
||||||
|
{"required": ["minecraft_java_monitoring_data"]},
|
||||||
|
{"required": ["minecraft_bedrock_monitoring_data"]},
|
||||||
|
{"properties": {"monitoring_type": {"const": "none"}}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersIndexHandler(BaseApiHandler):
|
||||||
|
def get(self):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: limit some columns for specific permissions
|
||||||
|
|
||||||
|
self.finish_json(200, {"status": "ok", "data": auth_data[0]})
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
_superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if EnumPermissionsCrafty.SERVER_CREATION not in exec_user_crafty_permissions:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||||
|
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, new_server_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
new_server_id, new_server_uuid = self.controller.create_api_server(data)
|
||||||
|
|
||||||
|
# Increase the server creation counter
|
||||||
|
self.controller.crafty_perms.add_server_creation(user["user_id"])
|
||||||
|
|
||||||
|
self.controller.stats.record_stats()
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
(
|
||||||
|
f"created server {data['name']}"
|
||||||
|
f" (ID: {new_server_id})"
|
||||||
|
f" (UUID: {new_server_uuid})"
|
||||||
|
),
|
||||||
|
server_id=new_server_id,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
201,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
"new_server_id": str(new_server_id),
|
||||||
|
"new_server_uuid": new_server_uuid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
98
app/classes/web/routes/api/servers/server/action.py
Normal file
98
app/classes/web/routes/api/servers/server/action.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
|
from app.classes.models.servers import Servers
|
||||||
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerActionHandler(BaseApiHandler):
|
||||||
|
def post(self, server_id: str, action: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if (
|
||||||
|
EnumPermissionsServer.COMMANDS
|
||||||
|
not in self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
auth_data[4]["user_id"], server_id
|
||||||
|
)
|
||||||
|
):
|
||||||
|
# if the user doesn't have Commands permission, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if action == "clone_server":
|
||||||
|
return self._clone_server(server_id, auth_data[4]["user_id"])
|
||||||
|
|
||||||
|
self.controller.management.send_command(
|
||||||
|
auth_data[4]["user_id"], server_id, self.get_remote_ip(), action
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _clone_server(self, server_id, user_id):
|
||||||
|
def is_name_used(name):
|
||||||
|
return Servers.select().where(Servers.server_name == name).count() != 0
|
||||||
|
|
||||||
|
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
|
server_uuid = server_data.get("server_uuid")
|
||||||
|
new_server_name = server_data.get("server_name") + " (Copy)"
|
||||||
|
|
||||||
|
name_counter = 1
|
||||||
|
while is_name_used(new_server_name):
|
||||||
|
name_counter += 1
|
||||||
|
new_server_name = server_data.get("server_name") + f" (Copy {name_counter})"
|
||||||
|
|
||||||
|
new_server_uuid = Helpers.create_uuid()
|
||||||
|
while os.path.exists(os.path.join(self.helper.servers_dir, new_server_uuid)):
|
||||||
|
new_server_uuid = Helpers.create_uuid()
|
||||||
|
new_server_path = os.path.join(self.helper.servers_dir, new_server_uuid)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user_id,
|
||||||
|
f"is cloning server {server_id} named {server_data.get('server_name')}",
|
||||||
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# copy the old server
|
||||||
|
FileHelpers.copy_dir(server_data.get("path"), new_server_path)
|
||||||
|
|
||||||
|
# TODO get old server DB data to individual variables
|
||||||
|
new_server_command = str(server_data.get("execution_command")).replace(
|
||||||
|
server_uuid, new_server_uuid
|
||||||
|
)
|
||||||
|
new_server_log_file = str(
|
||||||
|
self.helper.get_os_understandable_path(server_data.get("log_path"))
|
||||||
|
).replace(server_uuid, new_server_uuid)
|
||||||
|
|
||||||
|
new_server_id = self.controller.servers.create_server(
|
||||||
|
new_server_name,
|
||||||
|
new_server_uuid,
|
||||||
|
new_server_path,
|
||||||
|
"",
|
||||||
|
new_server_command,
|
||||||
|
server_data.get("executable"),
|
||||||
|
new_server_log_file,
|
||||||
|
server_data.get("stop_command"),
|
||||||
|
server_data.get("type"),
|
||||||
|
server_data.get("server_port"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.controller.init_all_servers()
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": {"new_server_id": str(new_server_id)}},
|
||||||
|
)
|
168
app/classes/web/routes/api/servers/server/index.py
Normal file
168
app/classes/web/routes/api/servers/server/index.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from jsonschema import validate
|
||||||
|
from jsonschema.exceptions import ValidationError
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# TODO: modify monitoring
|
||||||
|
server_patch_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"server_name": {"type": "string", "minLength": 1},
|
||||||
|
"path": {"type": "string", "minLength": 1},
|
||||||
|
"backup_path": {"type": "string"},
|
||||||
|
"executable": {"type": "string"},
|
||||||
|
"log_path": {"type": "string", "minLength": 1},
|
||||||
|
"execution_command": {"type": "string", "minLength": 1},
|
||||||
|
"auto_start": {"type": "boolean"},
|
||||||
|
"auto_start_delay": {"type": "integer"},
|
||||||
|
"crash_detection": {"type": "boolean"},
|
||||||
|
"stop_command": {"type": "string"},
|
||||||
|
"executable_update_url": {"type": "string", "minLength": 1},
|
||||||
|
"server_ip": {"type": "string", "minLength": 1},
|
||||||
|
"server_port": {"type": "integer"},
|
||||||
|
"logs_delete_after": {"type": "integer"},
|
||||||
|
"type": {"type": "string", "minLength": 1},
|
||||||
|
},
|
||||||
|
"anyOf": [
|
||||||
|
# Require at least one property
|
||||||
|
{"required": [name]}
|
||||||
|
for name in [
|
||||||
|
"server_name",
|
||||||
|
"path",
|
||||||
|
"backup_path",
|
||||||
|
"executable",
|
||||||
|
"log_path",
|
||||||
|
"execution_command",
|
||||||
|
"auto_start",
|
||||||
|
"auto_start_delay",
|
||||||
|
"crash_detection",
|
||||||
|
"stop_command",
|
||||||
|
"executable_update_url",
|
||||||
|
"server_ip",
|
||||||
|
"server_port",
|
||||||
|
"logs_delete_after",
|
||||||
|
"type",
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerIndexHandler(BaseApiHandler):
|
||||||
|
def get(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
|
server = model_to_dict(server_obj)
|
||||||
|
|
||||||
|
# TODO: limit some columns for specific permissions?
|
||||||
|
|
||||||
|
self.finish_json(200, {"status": "ok", "data": server})
|
||||||
|
|
||||||
|
def patch(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(self.request.body)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, server_patch_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if (
|
||||||
|
EnumPermissionsServer.CONFIG
|
||||||
|
not in self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
auth_data[4]["user_id"], server_id
|
||||||
|
)
|
||||||
|
):
|
||||||
|
# if the user doesn't have Config permission, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
|
for key in data:
|
||||||
|
# If we don't validate the input there could be security issues
|
||||||
|
setattr(self, key, data[key])
|
||||||
|
self.controller.servers.update_server(server_obj)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
auth_data[4]["user_id"],
|
||||||
|
f"modified the server with ID {server_id}",
|
||||||
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.finish_json(200, {"status": "ok"})
|
||||||
|
|
||||||
|
def delete(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
# DELETE /api/v2/servers/server?files=true
|
||||||
|
remove_files = self.get_query_argument("files", None) == "true"
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if (
|
||||||
|
EnumPermissionsServer.CONFIG
|
||||||
|
not in self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
auth_data[4]["user_id"], server_id
|
||||||
|
)
|
||||||
|
):
|
||||||
|
# if the user doesn't have Config permission, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
(
|
||||||
|
"Removing server and all associated files for server: "
|
||||||
|
if remove_files
|
||||||
|
else "Removing server from panel for server: "
|
||||||
|
)
|
||||||
|
+ self.controller.servers.get_server_friendly_name(server_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
|
self.controller.remove_server(server_id, remove_files)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
auth_data[4]["user_id"],
|
||||||
|
f"deleted the server {server_id}",
|
||||||
|
server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok"},
|
||||||
|
)
|
73
app/classes/web/routes/api/servers/server/logs.py
Normal file
73
app/classes/web/routes/api/servers/server/logs.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import html
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
|
from app.classes.shared.server import ServerOutBuf
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerLogsHandler(BaseApiHandler):
|
||||||
|
def get(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
# GET /api/v2/servers/server/logs?file=true
|
||||||
|
read_log_file = self.get_query_argument("file", None) == "true"
|
||||||
|
# GET /api/v2/servers/server/logs?colors=true
|
||||||
|
colored_output = self.get_query_argument("colors", None) == "true"
|
||||||
|
# GET /api/v2/servers/server/logs?raw=true
|
||||||
|
disable_ansi_strip = self.get_query_argument("raw", None) == "true"
|
||||||
|
# GET /api/v2/servers/server/logs?html=true
|
||||||
|
use_html = self.get_query_argument("html", None) == "true"
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if (
|
||||||
|
EnumPermissionsServer.LOGS
|
||||||
|
not in self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
auth_data[4]["user_id"], server_id
|
||||||
|
)
|
||||||
|
):
|
||||||
|
# if the user doesn't have Logs permission, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
|
|
||||||
|
if read_log_file:
|
||||||
|
log_lines = self.helper.get_setting("max_log_lines")
|
||||||
|
raw_lines = self.helper.tail_file(
|
||||||
|
self.helper.get_os_understandable_path(server_data["log_path"]),
|
||||||
|
log_lines,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raw_lines = ServerOutBuf.lines.get(server_id, [])
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
for line in raw_lines:
|
||||||
|
try:
|
||||||
|
if not disable_ansi_strip:
|
||||||
|
line = re.sub(
|
||||||
|
"(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )", "", line
|
||||||
|
)
|
||||||
|
line = re.sub("[A-z]{2}\b\b", "", line)
|
||||||
|
line = html.escape(line)
|
||||||
|
|
||||||
|
if colored_output:
|
||||||
|
line = self.helper.log_colors(line)
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Skipping Log Line due to error: {e}")
|
||||||
|
|
||||||
|
if use_html:
|
||||||
|
for line in lines:
|
||||||
|
self.write(f"{line}<br />")
|
||||||
|
else:
|
||||||
|
self.finish_json(200, {"status": "ok", "data": lines})
|
23
app/classes/web/routes/api/servers/server/public.py
Normal file
23
app/classes/web/routes/api/servers/server/public.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import logging
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerPublicHandler(BaseApiHandler):
|
||||||
|
def get(self, server_id):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": {
|
||||||
|
key: getattr(server_obj, key)
|
||||||
|
for key in ["server_id", "created", "server_name", "type"]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
28
app/classes/web/routes/api/servers/server/stats.py
Normal file
28
app/classes/web/routes/api/servers/server/stats.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import logging
|
||||||
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
from app.classes.models.server_stats import HelperServerStats
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerStatsHandler(BaseApiHandler):
|
||||||
|
def get(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": model_to_dict(
|
||||||
|
HelperServerStats.get_latest_server_stats(server_id)[0]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
31
app/classes/web/routes/api/servers/server/users.py
Normal file
31
app/classes/web/routes/api/servers/server/users.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import logging
|
||||||
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServersServerUsersHandler(BaseApiHandler):
|
||||||
|
def get(self, server_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if EnumPermissionsCrafty.USER_CONFIG not in auth_data[1]:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
if EnumPermissionsCrafty.ROLES_CONFIG not in auth_data[1]:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": list(self.controller.servers.get_authorized_users(server_id)),
|
||||||
|
},
|
||||||
|
)
|
164
app/classes/web/routes/api/users/index.py
Normal file
164
app/classes/web/routes/api/users/index.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from jsonschema import validate
|
||||||
|
from jsonschema.exceptions import ValidationError
|
||||||
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
|
from app.classes.models.roles import Roles, HelperRoles
|
||||||
|
from app.classes.models.users import PUBLIC_USER_ATTRS
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiUsersIndexHandler(BaseApiHandler):
|
||||||
|
def get(self):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
# GET /api/v2/users?ids=true
|
||||||
|
get_only_ids = self.get_query_argument("ids", None) == "true"
|
||||||
|
|
||||||
|
if EnumPermissionsCrafty.USER_CONFIG in exec_user_crafty_permissions:
|
||||||
|
if get_only_ids:
|
||||||
|
data = self.controller.users.get_all_user_ids()
|
||||||
|
else:
|
||||||
|
data = [
|
||||||
|
{key: getattr(user_res, key) for key in PUBLIC_USER_ATTRS}
|
||||||
|
for user_res in self.controller.users.get_all_users().execute()
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
if get_only_ids:
|
||||||
|
data = [user["user_id"]]
|
||||||
|
else:
|
||||||
|
user_res = self.controller.users.get_user_by_id(user["user_id"])
|
||||||
|
user_res["roles"] = list(
|
||||||
|
map(HelperRoles.get_role, user_res.get("roles", set()))
|
||||||
|
)
|
||||||
|
data = [{key: user_res[key] for key in PUBLIC_USER_ATTRS}]
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"data": data,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
new_user_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
**self.controller.users.user_jsonschema_props,
|
||||||
|
},
|
||||||
|
"required": ["username", "password"],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(self.request.body)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, new_user_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
username = data["username"]
|
||||||
|
password = data["password"]
|
||||||
|
email = data.get("email", "default@example.com")
|
||||||
|
enabled = data.get("enabled", True)
|
||||||
|
lang = data.get("lang", self.helper.get_setting("language"))
|
||||||
|
superuser = data.get("superuser", False)
|
||||||
|
permissions = data.get("permissions", None)
|
||||||
|
roles = data.get("roles", None)
|
||||||
|
hints = data.get("hints", True)
|
||||||
|
|
||||||
|
if username.lower() in ["system", ""]:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_USERNAME"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.controller.users.get_id_by_name(username) is not None:
|
||||||
|
return self.finish_json(400, {"status": "error", "error": "USER_EXISTS"})
|
||||||
|
|
||||||
|
if roles is None:
|
||||||
|
roles = set()
|
||||||
|
else:
|
||||||
|
role_ids = [str(role_id) for role_id in Roles.select(Roles.role_id)]
|
||||||
|
roles = {role for role in roles if str(role) in role_ids}
|
||||||
|
|
||||||
|
permissions_mask = "0" * len(EnumPermissionsCrafty.__members__.items())
|
||||||
|
server_quantity = {
|
||||||
|
perm.name: 0
|
||||||
|
for perm in self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
if permissions is not None:
|
||||||
|
server_quantity = {}
|
||||||
|
permissions_mask = list(permissions_mask)
|
||||||
|
for permission in permissions:
|
||||||
|
server_quantity[permission["name"]] = permission["quantity"]
|
||||||
|
permissions_mask[EnumPermissionsCrafty[permission["name"]].value] = (
|
||||||
|
"1" if permission["enabled"] else "0"
|
||||||
|
)
|
||||||
|
permissions_mask = "".join(permissions_mask)
|
||||||
|
|
||||||
|
# TODO: do this in the most efficient way
|
||||||
|
user_id = self.controller.users.add_user(
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
email,
|
||||||
|
enabled,
|
||||||
|
superuser,
|
||||||
|
)
|
||||||
|
self.controller.users.update_user(
|
||||||
|
user_id,
|
||||||
|
{"roles": roles, "lang": lang, "hints": hints},
|
||||||
|
{
|
||||||
|
"permissions_mask": permissions_mask,
|
||||||
|
"server_quantity": server_quantity,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
f"added user {username} (UID:{user_id}) with roles {roles}",
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
201,
|
||||||
|
{"status": "ok", "data": {"user_id": str(user_id)}},
|
||||||
|
)
|
241
app/classes/web/routes/api/users/user/index.py
Normal file
241
app/classes/web/routes/api/users/user/index.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from jsonschema import ValidationError, validate
|
||||||
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
|
from app.classes.models.roles import HelperRoles
|
||||||
|
from app.classes.models.users import HelperUsers
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||||
|
def get(self, user_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if user_id in ["@me", user["user_id"]]:
|
||||||
|
user_id = user["user_id"]
|
||||||
|
res_user = user
|
||||||
|
elif EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "NOT_AUTHORIZED",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# has User_Config permission and isn't viewing self
|
||||||
|
res_user = self.controller.users.get_user_by_id(user_id)
|
||||||
|
if not res_user:
|
||||||
|
return self.finish_json(
|
||||||
|
404,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "USER_NOT_FOUND",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove password and valid_tokens_from from the response
|
||||||
|
# as those should never be sent out to the client.
|
||||||
|
res_user.pop("password", None)
|
||||||
|
res_user.pop("valid_tokens_from", None)
|
||||||
|
res_user["roles"] = list(
|
||||||
|
map(HelperRoles.get_role, res_user.get("roles", set()))
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": res_user},
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self, user_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if (user_id in ["@me", user["user_id"]]) and self.helper.get_setting(
|
||||||
|
"allow_self_delete", False
|
||||||
|
):
|
||||||
|
user_id = user["user_id"]
|
||||||
|
self.controller.users.remove_user(user_id)
|
||||||
|
elif EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "NOT_AUTHORIZED",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# has User_Config permission
|
||||||
|
self.controller.users.remove_user(user_id)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
f"deleted the user {user_id}",
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def patch(self, user_id: str):
|
||||||
|
user_patch_schema = {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
**self.controller.users.user_jsonschema_props,
|
||||||
|
},
|
||||||
|
"anyOf": [
|
||||||
|
# Require at least one property
|
||||||
|
{"required": [name]}
|
||||||
|
for name in [
|
||||||
|
"username",
|
||||||
|
"password",
|
||||||
|
"email",
|
||||||
|
"enabled",
|
||||||
|
"lang",
|
||||||
|
"superuser",
|
||||||
|
"permissions",
|
||||||
|
"roles",
|
||||||
|
"hints",
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
exec_user_crafty_permissions,
|
||||||
|
_,
|
||||||
|
superuser,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(self.request.body)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate(data, user_patch_schema)
|
||||||
|
except ValidationError as e:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID_JSON_SCHEMA",
|
||||||
|
"error_data": str(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_id == "@me":
|
||||||
|
user_id = user["user_id"]
|
||||||
|
|
||||||
|
if (
|
||||||
|
EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions
|
||||||
|
and str(user["user_id"]) != str(user_id)
|
||||||
|
):
|
||||||
|
# If doesn't have perm can't edit other users
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "NOT_AUTHORIZED",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if data.get("username", None) is not None:
|
||||||
|
if data["username"].lower() in ["system", ""]:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_USERNAME"}
|
||||||
|
)
|
||||||
|
if self.controller.users.get_id_by_name(data["username"]) is not None:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "USER_EXISTS"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if data.get("superuser", None) is not None:
|
||||||
|
if str(user["user_id"]) == str(user_id):
|
||||||
|
# Checks if user is trying to change super user status of self.
|
||||||
|
# We don't want that.
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_SUPERUSER_MODIFY"}
|
||||||
|
)
|
||||||
|
if not superuser:
|
||||||
|
# The user is not superuser so they can't change the superuser status
|
||||||
|
data.pop("superuser")
|
||||||
|
|
||||||
|
if data.get("permissions", None) is not None:
|
||||||
|
if str(user["user_id"]) == str(user_id):
|
||||||
|
# Checks if user is trying to change permissions of self.
|
||||||
|
# We don't want that.
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"}
|
||||||
|
)
|
||||||
|
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||||
|
# Checks if user is trying to change permissions of someone
|
||||||
|
# else without User Config permission. We don't want that.
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if data.get("roles", None) is not None:
|
||||||
|
if str(user["user_id"]) == str(user_id):
|
||||||
|
# Checks if user is trying to change roles of self.
|
||||||
|
# We don't want that.
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_ROLES_MODIFY"}
|
||||||
|
)
|
||||||
|
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||||
|
# Checks if user is trying to change roles of someone
|
||||||
|
# else without User Config permission. We don't want that.
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "INVALID_ROLES_MODIFY"}
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: make this more efficient
|
||||||
|
# TODO: add permissions and roles because I forgot
|
||||||
|
user_obj = HelperUsers.get_user_model(user_id)
|
||||||
|
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
user["user_id"],
|
||||||
|
(
|
||||||
|
f"edited user {user_obj.username} (UID: {user_id})"
|
||||||
|
f"with roles {user_obj.roles}"
|
||||||
|
),
|
||||||
|
server_id=0,
|
||||||
|
source_ip=self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
for key in data:
|
||||||
|
# If we don't validate the input there could be security issues
|
||||||
|
setattr(user_obj, key, data[key])
|
||||||
|
user_obj.save()
|
||||||
|
|
||||||
|
return self.finish_json(200, {"status": "ok"})
|
49
app/classes/web/routes/api/users/user/pfp.py
Normal file
49
app/classes/web/routes/api/users/user/pfp.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import logging
|
||||||
|
import libgravatar
|
||||||
|
import requests
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiUsersUserPfpHandler(BaseApiHandler):
|
||||||
|
def get(self, user_id):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_id == "@me":
|
||||||
|
user = auth_data[4]
|
||||||
|
else:
|
||||||
|
user = self.controller.users.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f'User {auth_data[4]["user_id"]} is fetching the pfp for user {user_id}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# http://en.gravatar.com/site/implement/images/#rating
|
||||||
|
if self.helper.get_setting("allow_nsfw_profile_pictures"):
|
||||||
|
rating = "x"
|
||||||
|
else:
|
||||||
|
rating = "g"
|
||||||
|
|
||||||
|
# Get grvatar hash for profile pictures
|
||||||
|
if user["email"] != "default@example.com" or "":
|
||||||
|
gravatar = libgravatar.Gravatar(libgravatar.sanitize_email(user["email"]))
|
||||||
|
url = gravatar.get_image(
|
||||||
|
size=80,
|
||||||
|
default="404",
|
||||||
|
force_default=False,
|
||||||
|
rating=rating,
|
||||||
|
filetype_extension=False,
|
||||||
|
use_ssl=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
requests.head(url).raise_for_status()
|
||||||
|
except requests.HTTPError as e:
|
||||||
|
logger.debug("Gravatar profile picture not found", exc_info=e)
|
||||||
|
else:
|
||||||
|
self.finish_json(200, {"status": "ok", "data": url})
|
||||||
|
return
|
||||||
|
|
||||||
|
self.finish_json(200, {"status": "ok", "data": None})
|
37
app/classes/web/routes/api/users/user/public.py
Normal file
37
app/classes/web/routes/api/users/user/public.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import logging
|
||||||
|
from app.classes.models.roles import HelperRoles
|
||||||
|
from app.classes.models.users import PUBLIC_USER_ATTRS
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiUsersUserPublicHandler(BaseApiHandler):
|
||||||
|
def get(self, user_id: str):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
user,
|
||||||
|
) = auth_data
|
||||||
|
|
||||||
|
if user_id == "@me":
|
||||||
|
user_id = user["user_id"]
|
||||||
|
public_user = user
|
||||||
|
else:
|
||||||
|
public_user = self.controller.users.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
public_user = {key: public_user.get(key) for key in PUBLIC_USER_ATTRS}
|
||||||
|
|
||||||
|
public_user["roles"] = list(
|
||||||
|
map(HelperRoles.get_role, public_user.get("roles", set()))
|
||||||
|
)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{"status": "ok", "data": public_user},
|
||||||
|
)
|
@ -1,32 +1,29 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import tornado.web
|
||||||
|
import tornado.escape
|
||||||
|
import bleach
|
||||||
|
import libgravatar
|
||||||
|
import requests
|
||||||
|
|
||||||
from app.classes.minecraft.serverjars import server_jar_obj
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
from app.classes.models.crafty_permissions import Enum_Permissions_Crafty
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
from app.classes.shared.file_helpers import file_helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import tornado.web
|
|
||||||
import tornado.escape
|
|
||||||
import bleach
|
|
||||||
import libgravatar
|
|
||||||
import requests
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ServerHandler(BaseHandler):
|
|
||||||
|
|
||||||
|
class ServerHandler(BaseHandler):
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def get(self, page):
|
def get(self, page):
|
||||||
# pylint: disable=unused-variable
|
(
|
||||||
api_key, token_data, exec_user = self.current_user
|
api_key,
|
||||||
superuser = exec_user['superuser']
|
_token_data,
|
||||||
|
exec_user,
|
||||||
|
) = self.current_user
|
||||||
|
superuser = exec_user["superuser"]
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
@ -34,84 +31,123 @@ class ServerHandler(BaseHandler):
|
|||||||
if superuser:
|
if superuser:
|
||||||
defined_servers = self.controller.list_defined_servers()
|
defined_servers = self.controller.list_defined_servers()
|
||||||
exec_user_role.add("Super User")
|
exec_user_role.add("Super User")
|
||||||
exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions()
|
exec_user_crafty_permissions = (
|
||||||
|
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
|
)
|
||||||
list_roles = []
|
list_roles = []
|
||||||
for role in self.controller.roles.get_all_roles():
|
for role in self.controller.roles.get_all_roles():
|
||||||
list_roles.append(self.controller.roles.get_role(role.role_id))
|
list_roles.append(self.controller.roles.get_role(role.role_id))
|
||||||
else:
|
else:
|
||||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user["user_id"])
|
exec_user_crafty_permissions = (
|
||||||
defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"])
|
self.controller.crafty_perms.get_crafty_permissions_list(
|
||||||
|
exec_user["user_id"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
defined_servers = self.controller.servers.get_authorized_servers(
|
||||||
|
exec_user["user_id"]
|
||||||
|
)
|
||||||
list_roles = []
|
list_roles = []
|
||||||
for r in exec_user['roles']:
|
for r in exec_user["roles"]:
|
||||||
role = self.controller.roles.get_role(r)
|
role = self.controller.roles.get_role(r)
|
||||||
exec_user_role.add(role['role_name'])
|
exec_user_role.add(role["role_name"])
|
||||||
list_roles.append(self.controller.roles.get_role(role['role_id']))
|
list_roles.append(self.controller.roles.get_role(role["role_id"]))
|
||||||
|
|
||||||
template = "public/404.html"
|
template = "public/404.html"
|
||||||
|
|
||||||
page_data = {
|
page_data = {
|
||||||
'version_data': helper.get_version_string(),
|
"version_data": self.helper.get_version_string(),
|
||||||
'user_data': exec_user,
|
"user_data": exec_user,
|
||||||
'user_role' : exec_user_role,
|
"user_role": exec_user_role,
|
||||||
'roles' : list_roles,
|
"roles": list_roles,
|
||||||
'user_crafty_permissions' : exec_user_crafty_permissions,
|
"user_crafty_permissions": exec_user_crafty_permissions,
|
||||||
'crafty_permissions': {
|
"crafty_permissions": {
|
||||||
'Server_Creation': Enum_Permissions_Crafty.Server_Creation,
|
"Server_Creation": EnumPermissionsCrafty.SERVER_CREATION,
|
||||||
'User_Config': Enum_Permissions_Crafty.User_Config,
|
"User_Config": EnumPermissionsCrafty.USER_CONFIG,
|
||||||
'Roles_Config': Enum_Permissions_Crafty.Roles_Config,
|
"Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG,
|
||||||
},
|
},
|
||||||
'server_stats': {
|
"server_stats": {
|
||||||
'total': len(self.controller.list_defined_servers()),
|
"total": len(self.controller.list_defined_servers()),
|
||||||
'running': len(self.controller.list_running_servers()),
|
"running": len(self.controller.list_running_servers()),
|
||||||
'stopped': (len(self.controller.list_defined_servers()) - len(self.controller.list_running_servers()))
|
"stopped": (
|
||||||
|
len(self.controller.list_defined_servers())
|
||||||
|
- len(self.controller.list_running_servers())
|
||||||
|
),
|
||||||
},
|
},
|
||||||
'hosts_data': self.controller.management.get_latest_hosts_stats(),
|
"hosts_data": self.controller.management.get_latest_hosts_stats(),
|
||||||
'menu_servers': defined_servers,
|
"menu_servers": defined_servers,
|
||||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
"show_contribute": self.helper.get_setting("show_contribute_link", True),
|
||||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||||
'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"])),
|
"lang_page": Helpers.get_lang_page(
|
||||||
'api_key': {
|
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||||
'name': api_key.name,
|
),
|
||||||
'created': api_key.created,
|
"api_key": {
|
||||||
'server_permissions': api_key.server_permissions,
|
"name": api_key.name,
|
||||||
'crafty_permissions': api_key.crafty_permissions,
|
"created": api_key.created,
|
||||||
'superuser': api_key.superuser
|
"server_permissions": api_key.server_permissions,
|
||||||
} if api_key is not None else None,
|
"crafty_permissions": api_key.crafty_permissions,
|
||||||
'superuser': superuser
|
"superuser": api_key.superuser,
|
||||||
|
}
|
||||||
|
if api_key is not None
|
||||||
|
else None,
|
||||||
|
"superuser": superuser,
|
||||||
}
|
}
|
||||||
|
|
||||||
if helper.get_setting("allow_nsfw_profile_pictures"):
|
if self.helper.get_setting("allow_nsfw_profile_pictures"):
|
||||||
rating = "x"
|
rating = "x"
|
||||||
else:
|
else:
|
||||||
rating = "g"
|
rating = "g"
|
||||||
|
|
||||||
|
if exec_user["email"] != "default@example.com" or "":
|
||||||
if exec_user['email'] != 'default@example.com' or "":
|
gravatar = libgravatar.Gravatar(
|
||||||
g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user['email']))
|
libgravatar.sanitize_email(exec_user["email"])
|
||||||
url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404"
|
)
|
||||||
if requests.head(url).status_code != 404:
|
url = gravatar.get_image(
|
||||||
profile_url = url
|
size=80,
|
||||||
else:
|
default="404",
|
||||||
|
force_default=False,
|
||||||
|
rating=rating,
|
||||||
|
filetype_extension=False,
|
||||||
|
use_ssl=True,
|
||||||
|
) # + "?d=404"
|
||||||
|
try:
|
||||||
|
if requests.head(url).status_code != 404:
|
||||||
|
profile_url = url
|
||||||
|
else:
|
||||||
|
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||||
|
except:
|
||||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||||
else:
|
else:
|
||||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||||
|
|
||||||
page_data['user_image'] = profile_url
|
page_data["user_image"] = profile_url
|
||||||
if superuser:
|
if superuser:
|
||||||
page_data['roles'] = list_roles
|
page_data["roles"] = list_roles
|
||||||
|
|
||||||
if page == "step1":
|
if page == "step1":
|
||||||
if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]):
|
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||||
self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached")
|
exec_user["user_id"]
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Unauthorized access: "
|
||||||
|
"not a server creator or server limit reached"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
page_data['server_types'] = server_jar_obj.get_serverjar_data()
|
page_data["online"] = Helpers.check_internet()
|
||||||
page_data['js_server_types'] = json.dumps(server_jar_obj.get_serverjar_data())
|
page_data["server_types"] = self.controller.server_jars.get_serverjar_data()
|
||||||
|
page_data["js_server_types"] = json.dumps(
|
||||||
|
self.controller.server_jars.get_serverjar_data()
|
||||||
|
)
|
||||||
template = "server/wizard.html"
|
template = "server/wizard.html"
|
||||||
|
|
||||||
if page == "bedrock_step1":
|
if page == "bedrock_step1":
|
||||||
if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]):
|
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||||
self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached")
|
exec_user["user_id"]
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Unauthorized access: "
|
||||||
|
"not a server creator or server limit reached"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
template = "server/bedrock_wizard.html"
|
template = "server/bedrock_wizard.html"
|
||||||
@ -124,19 +160,20 @@ class ServerHandler(BaseHandler):
|
|||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def post(self, page):
|
def post(self, page):
|
||||||
# pylint: disable=unused-variable
|
api_key, _token_data, exec_user = self.current_user
|
||||||
api_key, token_data, exec_user = self.current_user
|
superuser = exec_user["superuser"]
|
||||||
superuser = exec_user['superuser']
|
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
|
|
||||||
template = "public/404.html"
|
template = "public/404.html"
|
||||||
page_data = {
|
page_data = {
|
||||||
'version_data': "version_data_here", # TODO
|
"version_data": "version_data_here", # TODO
|
||||||
'user_data': exec_user,
|
"user_data": exec_user,
|
||||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
"show_contribute": self.helper.get_setting("show_contribute_link", True),
|
||||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||||
'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"]))
|
"lang_page": Helpers.get_lang_page(
|
||||||
|
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
if page == "command":
|
if page == "command":
|
||||||
@ -145,53 +182,71 @@ class ServerHandler(BaseHandler):
|
|||||||
|
|
||||||
if server_id is not None:
|
if server_id is not None:
|
||||||
if command == "clone_server":
|
if command == "clone_server":
|
||||||
|
|
||||||
def is_name_used(name):
|
def is_name_used(name):
|
||||||
for server in self.controller.servers.get_all_defined_servers():
|
for server in self.controller.servers.get_all_defined_servers():
|
||||||
if server['server_name'] == name:
|
if server["server_name"] == name:
|
||||||
return True
|
return True
|
||||||
return
|
return
|
||||||
|
|
||||||
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
server_data = self.controller.servers.get_server_data_by_id(
|
||||||
server_uuid = server_data.get('server_uuid')
|
server_id
|
||||||
new_server_name = server_data.get('server_name') + " (Copy)"
|
)
|
||||||
|
server_uuid = server_data.get("server_uuid")
|
||||||
|
new_server_name = server_data.get("server_name") + " (Copy)"
|
||||||
|
|
||||||
name_counter = 1
|
name_counter = 1
|
||||||
while is_name_used(new_server_name):
|
while is_name_used(new_server_name):
|
||||||
name_counter += 1
|
name_counter += 1
|
||||||
new_server_name = server_data.get('server_name') + f" (Copy {name_counter})"
|
new_server_name = (
|
||||||
|
server_data.get("server_name") + f" (Copy {name_counter})"
|
||||||
|
)
|
||||||
|
|
||||||
new_server_uuid = helper.create_uuid()
|
new_server_uuid = Helpers.create_uuid()
|
||||||
while os.path.exists(os.path.join(helper.servers_dir, new_server_uuid)):
|
while os.path.exists(
|
||||||
new_server_uuid = helper.create_uuid()
|
os.path.join(self.helper.servers_dir, new_server_uuid)
|
||||||
new_server_path = os.path.join(helper.servers_dir, new_server_uuid)
|
):
|
||||||
|
new_server_uuid = Helpers.create_uuid()
|
||||||
|
new_server_path = os.path.join(
|
||||||
|
self.helper.servers_dir, new_server_uuid
|
||||||
|
)
|
||||||
|
|
||||||
# copy the old server
|
# copy the old server
|
||||||
file_helper.copy_dir(server_data.get('path'), new_server_path)
|
FileHelpers.copy_dir(server_data.get("path"), new_server_path)
|
||||||
|
|
||||||
# TODO get old server DB data to individual variables
|
# TODO get old server DB data to individual variables
|
||||||
stop_command = server_data.get('stop_command')
|
stop_command = server_data.get("stop_command")
|
||||||
new_server_command = str(server_data.get('execution_command')).replace(server_uuid, new_server_uuid)
|
new_server_command = str(
|
||||||
new_executable = server_data.get('executable')
|
server_data.get("execution_command")
|
||||||
new_server_log_file = str(helper.get_os_understandable_path(server_data.get('log_path'))).replace(server_uuid, new_server_uuid)
|
).replace(server_uuid, new_server_uuid)
|
||||||
server_port = server_data.get('server_port')
|
new_executable = server_data.get("executable")
|
||||||
server_type = server_data.get('type')
|
new_server_log_file = str(
|
||||||
|
Helpers.get_os_understandable_path(server_data.get("log_path"))
|
||||||
|
).replace(server_uuid, new_server_uuid)
|
||||||
|
backup_path = os.path.join(self.helper.backup_path, new_server_uuid)
|
||||||
|
server_port = server_data.get("server_port")
|
||||||
|
server_type = server_data.get("type")
|
||||||
|
|
||||||
self.controller.servers.create_server(new_server_name,
|
self.controller.servers.create_server(
|
||||||
new_server_uuid,
|
new_server_name,
|
||||||
new_server_path,
|
new_server_uuid,
|
||||||
"",
|
new_server_path,
|
||||||
new_server_command,
|
backup_path,
|
||||||
new_executable,
|
new_server_command,
|
||||||
new_server_log_file,
|
new_executable,
|
||||||
stop_command,
|
new_server_log_file,
|
||||||
server_type,
|
stop_command,
|
||||||
server_port)
|
server_type,
|
||||||
|
server_port,
|
||||||
|
)
|
||||||
|
|
||||||
self.controller.init_all_servers()
|
self.controller.init_all_servers()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), command)
|
self.controller.management.send_command(
|
||||||
|
exec_user["user_id"], server_id, self.get_remote_ip(), command
|
||||||
|
)
|
||||||
|
|
||||||
if page == "step1":
|
if page == "step1":
|
||||||
|
|
||||||
@ -199,82 +254,121 @@ class ServerHandler(BaseHandler):
|
|||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
else:
|
else:
|
||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
server = bleach.clean(self.get_argument('server', ''))
|
server = bleach.clean(self.get_argument("server", ""))
|
||||||
server_name = bleach.clean(self.get_argument('server_name', ''))
|
server_name = bleach.clean(self.get_argument("server_name", ""))
|
||||||
min_mem = bleach.clean(self.get_argument('min_memory', ''))
|
min_mem = bleach.clean(self.get_argument("min_memory", ""))
|
||||||
max_mem = bleach.clean(self.get_argument('max_memory', ''))
|
max_mem = bleach.clean(self.get_argument("max_memory", ""))
|
||||||
port = bleach.clean(self.get_argument('port', ''))
|
port = bleach.clean(self.get_argument("port", ""))
|
||||||
import_type = bleach.clean(self.get_argument('create_type', ''))
|
import_type = bleach.clean(self.get_argument("create_type", ""))
|
||||||
import_server_path = bleach.clean(self.get_argument('server_path', ''))
|
import_server_path = bleach.clean(self.get_argument("server_path", ""))
|
||||||
import_server_jar = bleach.clean(self.get_argument('server_jar', ''))
|
import_server_jar = bleach.clean(self.get_argument("server_jar", ""))
|
||||||
server_parts = server.split("|")
|
server_parts = server.split("|")
|
||||||
captured_roles = []
|
captured_roles = []
|
||||||
for role in user_roles:
|
for role in user_roles:
|
||||||
if bleach.clean(self.get_argument(str(role), '')) == "on":
|
if bleach.clean(self.get_argument(str(role), "")) == "on":
|
||||||
captured_roles.append(role)
|
captured_roles.append(role)
|
||||||
|
|
||||||
if not server_name:
|
if not server_name:
|
||||||
self.redirect("/panel/error?error=Server name cannot be empty!")
|
self.redirect("/panel/error?error=Server name cannot be empty!")
|
||||||
return
|
return
|
||||||
|
|
||||||
if import_type == 'import_jar':
|
if import_type == "import_jar":
|
||||||
good_path = self.controller.verify_jar_server(import_server_path, import_server_jar)
|
good_path = self.controller.verify_jar_server(
|
||||||
|
import_server_path, import_server_jar
|
||||||
|
)
|
||||||
|
|
||||||
if not good_path:
|
if not good_path:
|
||||||
self.redirect("/panel/error?error=Server path or Server Jar not found!")
|
self.redirect(
|
||||||
|
"/panel/error?error=Server path or Server Jar not found!"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
new_server_id = self.controller.import_jar_server(
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
server_name,
|
||||||
f"imported a jar server named \"{server_name}\"", # Example: Admin imported a server named "old creative"
|
import_server_path,
|
||||||
new_server_id,
|
import_server_jar,
|
||||||
self.get_remote_ip())
|
min_mem,
|
||||||
elif import_type == 'import_zip':
|
max_mem,
|
||||||
|
port,
|
||||||
|
)
|
||||||
|
self.controller.management.add_to_audit_log(
|
||||||
|
exec_user["user_id"],
|
||||||
|
f'imported a jar server named "{server_name}"',
|
||||||
|
new_server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
elif import_type == "import_zip":
|
||||||
# here import_server_path means the zip path
|
# here import_server_path means the zip path
|
||||||
zip_path = bleach.clean(self.get_argument('root_path'))
|
zip_path = bleach.clean(self.get_argument("root_path"))
|
||||||
good_path = helper.check_path_exists(zip_path)
|
good_path = Helpers.check_path_exists(zip_path)
|
||||||
if not good_path:
|
if not good_path:
|
||||||
self.redirect("/panel/error?error=Temp path not found!")
|
self.redirect("/panel/error?error=Temp path not found!")
|
||||||
return
|
return
|
||||||
|
|
||||||
new_server_id = self.controller.import_zip_server(server_name, zip_path, import_server_jar, min_mem, max_mem, port)
|
new_server_id = self.controller.import_zip_server(
|
||||||
|
server_name, zip_path, import_server_jar, min_mem, max_mem, port
|
||||||
|
)
|
||||||
if new_server_id == "false":
|
if new_server_id == "false":
|
||||||
self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with" +
|
self.redirect(
|
||||||
f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}")
|
f"/panel/error?error=Zip file not accessible! "
|
||||||
|
f"You can fix this permissions issue with "
|
||||||
|
f"sudo chown -R crafty:crafty {import_server_path} "
|
||||||
|
f"And sudo chmod 2775 -R {import_server_path}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
self.controller.management.add_to_audit_log(
|
||||||
f"imported a zip server named \"{server_name}\"", # Example: Admin imported a server named "old creative"
|
exec_user["user_id"],
|
||||||
new_server_id,
|
f'imported a zip server named "{server_name}"',
|
||||||
self.get_remote_ip())
|
new_server_id,
|
||||||
#deletes temp dir
|
self.get_remote_ip(),
|
||||||
file_helper.del_dirs(zip_path)
|
)
|
||||||
|
# deletes temp dir
|
||||||
|
FileHelpers.del_dirs(zip_path)
|
||||||
else:
|
else:
|
||||||
if len(server_parts) != 2:
|
if len(server_parts) != 2:
|
||||||
self.redirect("/panel/error?error=Invalid server data")
|
self.redirect("/panel/error?error=Invalid server data")
|
||||||
return
|
return
|
||||||
server_type, server_version = server_parts
|
server_type, server_version = server_parts
|
||||||
# TODO: add server type check here and call the correct server add functions if not a jar
|
# TODO: add server type check here and call the correct server
|
||||||
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
|
# add functions if not a jar
|
||||||
new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port)
|
new_server_id = self.controller.create_jar_server(
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
server_type, server_version, server_name, min_mem, max_mem, port
|
||||||
f"created a {server_version} {str(server_type).capitalize()} server named \"{server_name}\"",
|
)
|
||||||
# Example: Admin created a 1.16.5 Bukkit server named "survival"
|
self.controller.management.add_to_audit_log(
|
||||||
new_server_id,
|
exec_user["user_id"],
|
||||||
self.get_remote_ip())
|
f"created a {server_version} {str(server_type).capitalize()}"
|
||||||
|
f' server named "{server_name}"',
|
||||||
|
# Example: Admin created a 1.16.5 Bukkit server named "survival"
|
||||||
|
new_server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
# These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser
|
# These lines create a new Role for the Server with full permissions
|
||||||
|
# and add the user to it if he's not a superuser
|
||||||
if len(captured_roles) == 0:
|
if len(captured_roles) == 0:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid")
|
new_server_uuid = self.controller.servers.get_server_data_by_id(
|
||||||
role_id = self.controller.roles.add_role(f"Creator of Server with uuid={new_server_uuid}")
|
new_server_id
|
||||||
self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
).get("server_uuid")
|
||||||
self.controller.users.add_role_to_user(exec_user["user_id"], role_id)
|
role_id = self.controller.roles.add_role(
|
||||||
self.controller.crafty_perms.add_server_creation(exec_user["user_id"])
|
f"Creator of Server with uuid={new_server_uuid}"
|
||||||
|
)
|
||||||
|
self.controller.server_perms.add_role_server(
|
||||||
|
new_server_id, role_id, "11111111"
|
||||||
|
)
|
||||||
|
self.controller.users.add_role_to_user(
|
||||||
|
exec_user["user_id"], role_id
|
||||||
|
)
|
||||||
|
self.controller.crafty_perms.add_server_creation(
|
||||||
|
exec_user["user_id"]
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for role in captured_roles:
|
for role in captured_roles:
|
||||||
role_id = role
|
role_id = role
|
||||||
self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
self.controller.server_perms.add_role_server(
|
||||||
|
new_server_id, role_id, "11111111"
|
||||||
|
)
|
||||||
|
|
||||||
self.controller.stats.record_stats()
|
self.controller.stats.record_stats()
|
||||||
self.redirect("/panel/dashboard")
|
self.redirect("/panel/dashboard")
|
||||||
@ -284,80 +378,114 @@ class ServerHandler(BaseHandler):
|
|||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
else:
|
else:
|
||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
server = bleach.clean(self.get_argument('server', ''))
|
server = bleach.clean(self.get_argument("server", ""))
|
||||||
server_name = bleach.clean(self.get_argument('server_name', ''))
|
server_name = bleach.clean(self.get_argument("server_name", ""))
|
||||||
port = bleach.clean(self.get_argument('port', ''))
|
port = bleach.clean(self.get_argument("port", ""))
|
||||||
import_type = bleach.clean(self.get_argument('create_type', ''))
|
import_type = bleach.clean(self.get_argument("create_type", ""))
|
||||||
import_server_path = bleach.clean(self.get_argument('server_path', ''))
|
import_server_path = bleach.clean(self.get_argument("server_path", ""))
|
||||||
import_server_exe = bleach.clean(self.get_argument('server_jar', ''))
|
import_server_exe = bleach.clean(self.get_argument("server_jar", ""))
|
||||||
server_parts = server.split("|")
|
server_parts = server.split("|")
|
||||||
captured_roles = []
|
captured_roles = []
|
||||||
for role in user_roles:
|
for role in user_roles:
|
||||||
if bleach.clean(self.get_argument(str(role), '')) == "on":
|
if bleach.clean(self.get_argument(str(role), "")) == "on":
|
||||||
captured_roles.append(role)
|
captured_roles.append(role)
|
||||||
|
|
||||||
if not server_name:
|
if not server_name:
|
||||||
self.redirect("/panel/error?error=Server name cannot be empty!")
|
self.redirect("/panel/error?error=Server name cannot be empty!")
|
||||||
return
|
return
|
||||||
|
|
||||||
if import_type == 'import_jar':
|
if import_type == "import_jar":
|
||||||
good_path = self.controller.verify_jar_server(import_server_path, import_server_exe)
|
good_path = self.controller.verify_jar_server(
|
||||||
|
import_server_path, import_server_exe
|
||||||
|
)
|
||||||
|
|
||||||
if not good_path:
|
if not good_path:
|
||||||
self.redirect("/panel/error?error=Server path or Server Jar not found!")
|
self.redirect(
|
||||||
|
"/panel/error?error=Server path or Server Jar not found!"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_server_id = self.controller.import_bedrock_server(server_name, import_server_path,import_server_exe, port)
|
new_server_id = self.controller.import_bedrock_server(
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
server_name, import_server_path, import_server_exe, port
|
||||||
f"imported a jar server named \"{server_name}\"", # Example: Admin imported a server named "old creative"
|
)
|
||||||
new_server_id,
|
self.controller.management.add_to_audit_log(
|
||||||
self.get_remote_ip())
|
exec_user["user_id"],
|
||||||
elif import_type == 'import_zip':
|
f'imported a jar server named "{server_name}"',
|
||||||
|
new_server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
elif import_type == "import_zip":
|
||||||
# here import_server_path means the zip path
|
# here import_server_path means the zip path
|
||||||
zip_path = bleach.clean(self.get_argument('root_path'))
|
zip_path = bleach.clean(self.get_argument("root_path"))
|
||||||
good_path = helper.check_path_exists(zip_path)
|
good_path = Helpers.check_path_exists(zip_path)
|
||||||
if not good_path:
|
if not good_path:
|
||||||
self.redirect("/panel/error?error=Temp path not found!")
|
self.redirect("/panel/error?error=Temp path not found!")
|
||||||
return
|
return
|
||||||
|
|
||||||
new_server_id = self.controller.import_bedrock_zip_server(server_name, zip_path, import_server_exe, port)
|
new_server_id = self.controller.import_bedrock_zip_server(
|
||||||
|
server_name, zip_path, import_server_exe, port
|
||||||
|
)
|
||||||
if new_server_id == "false":
|
if new_server_id == "false":
|
||||||
self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with" +
|
self.redirect(
|
||||||
f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}")
|
f"/panel/error?error=Zip file not accessible! "
|
||||||
|
f"You can fix this permissions issue with"
|
||||||
|
f"sudo chown -R crafty:crafty {import_server_path} "
|
||||||
|
f"And sudo chmod 2775 -R {import_server_path}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
self.controller.management.add_to_audit_log(
|
||||||
f"imported a zip server named \"{server_name}\"", # Example: Admin imported a server named "old creative"
|
exec_user["user_id"],
|
||||||
new_server_id,
|
f'imported a zip server named "{server_name}"',
|
||||||
self.get_remote_ip())
|
new_server_id,
|
||||||
#deletes temp dir
|
self.get_remote_ip(),
|
||||||
file_helper.del_dirs(zip_path)
|
)
|
||||||
|
# deletes temp dir
|
||||||
|
FileHelpers.del_dirs(zip_path)
|
||||||
else:
|
else:
|
||||||
if len(server_parts) != 2:
|
if len(server_parts) != 2:
|
||||||
self.redirect("/panel/error?error=Invalid server data")
|
self.redirect("/panel/error?error=Invalid server data")
|
||||||
return
|
return
|
||||||
server_type, server_version = server_parts
|
server_type, server_version = server_parts
|
||||||
# TODO: add server type check here and call the correct server add functions if not a jar
|
# TODO: add server type check here and call the correct server
|
||||||
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
|
# add functions if not a jar
|
||||||
new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port)
|
new_server_id = self.controller.create_jar_server(
|
||||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
server_type, server_version, server_name, min_mem, max_mem, port
|
||||||
f"created a {server_version} {str(server_type).capitalize()} server named \"{server_name}\"",
|
)
|
||||||
# Example: Admin created a 1.16.5 Bukkit server named "survival"
|
self.controller.management.add_to_audit_log(
|
||||||
new_server_id,
|
exec_user["user_id"],
|
||||||
self.get_remote_ip())
|
f"created a {server_version} {str(server_type).capitalize()} "
|
||||||
|
f'server named "{server_name}"',
|
||||||
|
# Example: Admin created a 1.16.5 Bukkit server named "survival"
|
||||||
|
new_server_id,
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
|
||||||
# These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser
|
# These lines create a new Role for the Server with full permissions
|
||||||
|
# and add the user to it if he's not a superuser
|
||||||
if len(captured_roles) == 0:
|
if len(captured_roles) == 0:
|
||||||
if not superuser:
|
if not superuser:
|
||||||
new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid")
|
new_server_uuid = self.controller.servers.get_server_data_by_id(
|
||||||
role_id = self.controller.roles.add_role(f"Creator of Server with uuid={new_server_uuid}")
|
new_server_id
|
||||||
self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
).get("server_uuid")
|
||||||
self.controller.users.add_role_to_user(exec_user["user_id"], role_id)
|
role_id = self.controller.roles.add_role(
|
||||||
self.controller.crafty_perms.add_server_creation(exec_user["user_id"])
|
f"Creator of Server with uuid={new_server_uuid}"
|
||||||
|
)
|
||||||
|
self.controller.server_perms.add_role_server(
|
||||||
|
new_server_id, role_id, "11111111"
|
||||||
|
)
|
||||||
|
self.controller.users.add_role_to_user(
|
||||||
|
exec_user["user_id"], role_id
|
||||||
|
)
|
||||||
|
self.controller.crafty_perms.add_server_creation(
|
||||||
|
exec_user["user_id"]
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for role in captured_roles:
|
for role in captured_roles:
|
||||||
role_id = role
|
role_id = role
|
||||||
self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
self.controller.server_perms.add_role_server(
|
||||||
|
new_server_id, role_id, "11111111"
|
||||||
|
)
|
||||||
|
|
||||||
self.controller.stats.record_stats()
|
self.controller.stats.record_stats()
|
||||||
self.redirect("/panel/dashboard")
|
self.redirect("/panel/dashboard")
|
||||||
@ -369,4 +497,4 @@ class ServerHandler(BaseHandler):
|
|||||||
translate=self.translator.translate,
|
translate=self.translator.translate,
|
||||||
)
|
)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
self.redirect('/panel/dashboard')
|
self.redirect("/panel/dashboard")
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
from typing import ( Optional )
|
from typing import Optional
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tornado.web
|
import tornado.web
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.helpers import helper
|
||||||
|
|
||||||
helper.auto_installer_fix(e)
|
helper.auto_installer_fix(e)
|
||||||
|
|
||||||
|
|
||||||
class CustomStaticHandler(tornado.web.StaticFileHandler):
|
class CustomStaticHandler(tornado.web.StaticFileHandler):
|
||||||
def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
|
def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
|
||||||
try:
|
try:
|
||||||
return super().validate_absolute_path(root, absolute_path)
|
return super().validate_absolute_path(root, absolute_path)
|
||||||
except tornado.web.HTTPError as error:
|
except tornado.web.HTTPError as error:
|
||||||
if 'HTTP 404: Not Found' in str(error):
|
if "HTTP 404: Not Found" in str(error):
|
||||||
self.set_status(404)
|
self.set_status(404)
|
||||||
self.finish({'error':'NOT_FOUND', 'info':'The requested resource was not found on the server'})
|
self.finish(
|
||||||
|
{
|
||||||
|
"error": "NOT_FOUND",
|
||||||
|
"info": "The requested resource was not found on the server",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -1,46 +1,53 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class StatusHandler(BaseHandler):
|
class StatusHandler(BaseHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
page_data = {}
|
page_data = {}
|
||||||
page_data['lang'] = helper.get_setting('language')
|
page_data["lang"] = self.helper.get_setting("language")
|
||||||
page_data['lang_page'] = helper.getLangPage(helper.get_setting('language'))
|
page_data["lang_page"] = self.helper.get_lang_page(
|
||||||
page_data['servers'] = self.controller.servers.get_all_servers_stats()
|
self.helper.get_setting("language")
|
||||||
|
)
|
||||||
|
page_data["servers"] = self.controller.servers.get_all_servers_stats()
|
||||||
running = 0
|
running = 0
|
||||||
for srv in page_data['servers']:
|
for srv in page_data["servers"]:
|
||||||
if srv['stats']['running']:
|
if srv["stats"]["running"]:
|
||||||
running += 1
|
running += 1
|
||||||
server_data = srv.get('server_data', False)
|
server_data = srv.get("server_data", False)
|
||||||
server_id = server_data.get('server_id', False)
|
server_id = server_data.get("server_id", False)
|
||||||
srv['raw_ping_result'] = self.controller.servers.get_server_stats_by_id(server_id)
|
srv["raw_ping_result"] = self.controller.servers.get_server_stats_by_id(
|
||||||
if 'icon' not in srv['raw_ping_result']:
|
server_id
|
||||||
srv['raw_ping_result']['icon'] = False
|
)
|
||||||
|
if "icon" not in srv["raw_ping_result"]:
|
||||||
|
srv["raw_ping_result"]["icon"] = False
|
||||||
|
|
||||||
page_data['running'] = running
|
page_data["running"] = running
|
||||||
|
|
||||||
template = 'public/status.html'
|
template = "public/status.html"
|
||||||
|
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
data=page_data,
|
data=page_data,
|
||||||
translate=self.translator.translate,
|
translate=self.translator.translate,
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
page_data = {}
|
page_data = {}
|
||||||
page_data['servers'] = self.controller.servers.get_all_servers_stats()
|
page_data["servers"] = self.controller.servers.get_all_servers_stats()
|
||||||
for srv in page_data['servers']:
|
for srv in page_data["servers"]:
|
||||||
server_data = srv.get('server_data', False)
|
server_data = srv.get("server_data", False)
|
||||||
server_id = server_data.get('server_id', False)
|
server_id = server_data.get("server_id", False)
|
||||||
srv['raw_ping_result'] = self.controller.servers.get_server_stats_by_id(server_id)
|
srv["raw_ping_result"] = self.controller.servers.get_server_stats_by_id(
|
||||||
template = 'public/status.html'
|
server_id
|
||||||
|
)
|
||||||
|
template = "public/status.html"
|
||||||
|
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
data=page_data,
|
data=page_data,
|
||||||
translate=self.translator.translate,
|
translate=self.translator.translate,
|
||||||
)
|
)
|
||||||
|
@ -3,43 +3,55 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import tornado.web
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.log
|
||||||
|
import tornado.template
|
||||||
|
import tornado.escape
|
||||||
|
import tornado.locale
|
||||||
|
import tornado.httpserver
|
||||||
|
|
||||||
from app.classes.shared.translation import translation
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.main_controller import Controller
|
||||||
from app.classes.web.file_handler import FileHandler
|
from app.classes.web.file_handler import FileHandler
|
||||||
from app.classes.web.public_handler import PublicHandler
|
from app.classes.web.public_handler import PublicHandler
|
||||||
from app.classes.web.panel_handler import PanelHandler
|
from app.classes.web.panel_handler import PanelHandler
|
||||||
from app.classes.web.default_handler import DefaultHandler
|
from app.classes.web.default_handler import DefaultHandler
|
||||||
|
from app.classes.web.routes.api.api_handlers import api_handlers
|
||||||
from app.classes.web.server_handler import ServerHandler
|
from app.classes.web.server_handler import ServerHandler
|
||||||
from app.classes.web.ajax_handler import AjaxHandler
|
from app.classes.web.ajax_handler import AjaxHandler
|
||||||
from app.classes.web.api_handler import ServersStats, NodeStats
|
from app.classes.web.api_handler import (
|
||||||
|
ServersStats,
|
||||||
|
NodeStats,
|
||||||
|
ServerBackup,
|
||||||
|
StartServer,
|
||||||
|
StopServer,
|
||||||
|
RestartServer,
|
||||||
|
CreateUser,
|
||||||
|
DeleteUser,
|
||||||
|
ListServers,
|
||||||
|
SendCommand,
|
||||||
|
)
|
||||||
from app.classes.web.websocket_handler import SocketHandler
|
from app.classes.web.websocket_handler import SocketHandler
|
||||||
from app.classes.web.static_handler import CustomStaticHandler
|
from app.classes.web.static_handler import CustomStaticHandler
|
||||||
from app.classes.web.upload_handler import UploadHandler
|
from app.classes.web.upload_handler import UploadHandler
|
||||||
from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage
|
from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage
|
||||||
from app.classes.web.status_handler import StatusHandler
|
from app.classes.web.status_handler import StatusHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import tornado.web
|
|
||||||
import tornado.ioloop
|
|
||||||
import tornado.log
|
|
||||||
import tornado.template
|
|
||||||
import tornado.escape
|
|
||||||
import tornado.locale
|
|
||||||
import tornado.httpserver
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Webserver:
|
|
||||||
|
|
||||||
def __init__(self, controller, tasks_manager):
|
class Webserver:
|
||||||
|
controller: Controller
|
||||||
|
helper: Helpers
|
||||||
|
|
||||||
|
def __init__(self, helper, controller, tasks_manager):
|
||||||
self.ioloop = None
|
self.ioloop = None
|
||||||
self.HTTP_Server = None
|
self.http_server = None
|
||||||
self.HTTPS_Server = None
|
self.https_server = None
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.tasks_manager = tasks_manager
|
self.tasks_manager = tasks_manager
|
||||||
self._asyncio_patch()
|
self._asyncio_patch()
|
||||||
@ -48,12 +60,12 @@ class Webserver:
|
|||||||
def log_function(handler):
|
def log_function(handler):
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
'Status_Code': handler.get_status(),
|
"Status_Code": handler.get_status(),
|
||||||
'Method': handler.request.method,
|
"Method": handler.request.method,
|
||||||
'URL': handler.request.uri,
|
"URL": handler.request.uri,
|
||||||
'Remote_IP': handler.request.remote_ip,
|
"Remote_IP": handler.request.remote_ip,
|
||||||
# pylint: disable=consider-using-f-string
|
# pylint: disable=consider-using-f-string
|
||||||
'Elapsed_Time': '%.2fms' % (handler.request.request_time() * 1000)
|
"Elapsed_Time": "%.2fms" % (handler.request.request_time() * 1000),
|
||||||
}
|
}
|
||||||
|
|
||||||
tornado.log.access_log.info(json.dumps(info, indent=4))
|
tornado.log.access_log.info(json.dumps(info, indent=4))
|
||||||
@ -61,39 +73,47 @@ class Webserver:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _asyncio_patch():
|
def _asyncio_patch():
|
||||||
"""
|
"""
|
||||||
As of Python 3.8 (on Windows), the asyncio default event handler has changed to "proactor",
|
As of Python 3.8 (on Windows),
|
||||||
|
the asyncio default event handler has changed to "proactor",
|
||||||
where tornado expects the "selector" handler.
|
where tornado expects the "selector" handler.
|
||||||
|
|
||||||
This function checks if the platform is windows and changes the event handler to suit.
|
This function checks if the platform is windows and
|
||||||
|
changes the event handler to suit.
|
||||||
|
|
||||||
(Taken from https://github.com/mkdocs/mkdocs/commit/cf2b136d4257787c0de51eba2d9e30ded5245b31)
|
(Taken from
|
||||||
|
https://github.com/mkdocs/mkdocs/commit/cf2b136d4257787c0de51eba2d9e30ded5245b31)
|
||||||
"""
|
"""
|
||||||
logger.debug("Checking if asyncio patch is required")
|
logger.debug("Checking if asyncio patch is required")
|
||||||
if sys.platform.startswith("win") and sys.version_info >= (3, 8):
|
if sys.platform.startswith("win") and sys.version_info >= (3, 8):
|
||||||
# pylint: disable=reimported,import-outside-toplevel,redefined-outer-name
|
# pylint: disable=reimported,import-outside-toplevel,redefined-outer-name
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from asyncio import WindowsSelectorEventLoopPolicy
|
from asyncio import WindowsSelectorEventLoopPolicy
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.debug("asyncio patch isn't required") # Can't assign a policy which doesn't exist.
|
logger.debug(
|
||||||
|
"asyncio patch isn't required"
|
||||||
|
) # Can't assign a policy which doesn't exist.
|
||||||
else:
|
else:
|
||||||
if not isinstance(asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy):
|
if not isinstance(
|
||||||
|
asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy
|
||||||
|
):
|
||||||
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
|
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
|
||||||
logger.debug("Applied asyncio patch")
|
logger.debug("Applied asyncio patch")
|
||||||
|
|
||||||
def run_tornado(self):
|
def run_tornado(self):
|
||||||
|
|
||||||
# let's verify we have an SSL cert
|
# let's verify we have an SSL cert
|
||||||
helper.create_self_signed_cert()
|
self.helper.create_self_signed_cert()
|
||||||
|
|
||||||
http_port = helper.get_setting('http_port')
|
http_port = self.helper.get_setting("http_port")
|
||||||
https_port = helper.get_setting('https_port')
|
https_port = self.helper.get_setting("https_port")
|
||||||
|
|
||||||
debug_errors = helper.get_setting('show_errors')
|
debug_errors = self.helper.get_setting("show_errors")
|
||||||
cookie_secret = helper.get_setting('cookie_secret')
|
cookie_secret = self.helper.get_setting("cookie_secret")
|
||||||
|
|
||||||
if cookie_secret is False:
|
if cookie_secret is False:
|
||||||
cookie_secret = helper.random_string_generator(32)
|
cookie_secret = self.helper.random_string_generator(32)
|
||||||
|
|
||||||
if not http_port:
|
if not http_port:
|
||||||
http_port = 8000
|
http_port = 8000
|
||||||
@ -102,38 +122,58 @@ class Webserver:
|
|||||||
https_port = 8443
|
https_port = 8443
|
||||||
|
|
||||||
cert_objects = {
|
cert_objects = {
|
||||||
'certfile': os.path.join(helper.config_dir, 'web', 'certs', 'commander.cert.pem'),
|
"certfile": os.path.join(
|
||||||
'keyfile': os.path.join(helper.config_dir, 'web', 'certs', 'commander.key.pem'),
|
self.helper.config_dir, "web", "certs", "commander.cert.pem"
|
||||||
|
),
|
||||||
|
"keyfile": os.path.join(
|
||||||
|
self.helper.config_dir, "web", "certs", "commander.key.pem"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(f"Starting Web Server on ports http:{http_port} https:{https_port}")
|
logger.info(f"Starting Web Server on ports http:{http_port} https:{https_port}")
|
||||||
|
|
||||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||||
|
|
||||||
tornado.template.Loader('.')
|
tornado.template.Loader(".")
|
||||||
|
|
||||||
# TODO: Remove because we don't and won't use
|
# TODO: Remove because we don't and won't use
|
||||||
tornado.locale.set_default_locale('en_EN')
|
tornado.locale.set_default_locale("en_EN")
|
||||||
|
|
||||||
handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager, "translator": translation}
|
handler_args = {
|
||||||
|
"helper": self.helper,
|
||||||
|
"controller": self.controller,
|
||||||
|
"tasks_manager": self.tasks_manager,
|
||||||
|
"translator": self.helper.translation,
|
||||||
|
}
|
||||||
handlers = [
|
handlers = [
|
||||||
(r'/', DefaultHandler, handler_args),
|
(r"/", DefaultHandler, handler_args),
|
||||||
(r'/public/(.*)', PublicHandler, handler_args),
|
(r"/public/(.*)", PublicHandler, handler_args),
|
||||||
(r'/panel/(.*)', PanelHandler, handler_args),
|
(r"/panel/(.*)", PanelHandler, handler_args),
|
||||||
(r'/server/(.*)', ServerHandler, handler_args),
|
(r"/server/(.*)", ServerHandler, handler_args),
|
||||||
(r'/ajax/(.*)', AjaxHandler, handler_args),
|
(r"/ajax/(.*)", AjaxHandler, handler_args),
|
||||||
(r'/files/(.*)', FileHandler, handler_args),
|
(r"/files/(.*)", FileHandler, handler_args),
|
||||||
(r'/api/stats/servers', ServersStats, handler_args),
|
(r"/ws", SocketHandler, handler_args),
|
||||||
(r'/api/stats/node', NodeStats, handler_args),
|
(r"/upload", UploadHandler, handler_args),
|
||||||
(r'/ws', SocketHandler, handler_args),
|
(r"/status", StatusHandler, handler_args),
|
||||||
(r'/upload', UploadHandler, handler_args),
|
# API Routes V1
|
||||||
(r'/status', StatusHandler, handler_args)
|
(r"/api/v1/stats/servers", ServersStats, handler_args),
|
||||||
]
|
(r"/api/v1/stats/node", NodeStats, handler_args),
|
||||||
|
(r"/api/v1/server/send_command", SendCommand, handler_args),
|
||||||
|
(r"/api/v1/server/backup", ServerBackup, handler_args),
|
||||||
|
(r"/api/v1/server/start", StartServer, handler_args),
|
||||||
|
(r"/api/v1/server/stop", StopServer, handler_args),
|
||||||
|
(r"/api/v1/server/restart", RestartServer, handler_args),
|
||||||
|
(r"/api/v1/list_servers", ListServers, handler_args),
|
||||||
|
(r"/api/v1/users/create_user", CreateUser, handler_args),
|
||||||
|
(r"/api/v1/users/delete_user", DeleteUser, handler_args),
|
||||||
|
# API Routes V2
|
||||||
|
*api_handlers(handler_args),
|
||||||
|
]
|
||||||
|
|
||||||
app = tornado.web.Application(
|
app = tornado.web.Application(
|
||||||
handlers,
|
handlers,
|
||||||
template_path=os.path.join(helper.webroot, 'templates'),
|
template_path=os.path.join(self.helper.webroot, "templates"),
|
||||||
static_path=os.path.join(helper.webroot, 'static'),
|
static_path=os.path.join(self.helper.webroot, "static"),
|
||||||
debug=debug_errors,
|
debug=debug_errors,
|
||||||
cookie_secret=cookie_secret,
|
cookie_secret=cookie_secret,
|
||||||
xsrf_cookies=True,
|
xsrf_cookies=True,
|
||||||
@ -144,48 +184,56 @@ class Webserver:
|
|||||||
static_handler_class=CustomStaticHandler,
|
static_handler_class=CustomStaticHandler,
|
||||||
serve_traceback=debug_errors,
|
serve_traceback=debug_errors,
|
||||||
)
|
)
|
||||||
HTTPhanders = [(r'/', HTTPHandler, handler_args),
|
http_handers = [
|
||||||
(r'/public/(.*)', HTTPHandlerPage, handler_args),
|
(r"/", HTTPHandler, handler_args),
|
||||||
(r'/panel/(.*)', HTTPHandlerPage, handler_args),
|
(r"/public/(.*)", HTTPHandlerPage, handler_args),
|
||||||
(r'/server/(.*)', HTTPHandlerPage, handler_args),
|
(r"/panel/(.*)", HTTPHandlerPage, handler_args),
|
||||||
(r'/ajax/(.*)', HTTPHandlerPage, handler_args),
|
(r"/server/(.*)", HTTPHandlerPage, handler_args),
|
||||||
(r'/api/stats/servers', HTTPHandlerPage, handler_args),
|
(r"/ajax/(.*)", HTTPHandlerPage, handler_args),
|
||||||
(r'/api/stats/node', HTTPHandlerPage, handler_args),
|
(r"/api/stats/servers", HTTPHandlerPage, handler_args),
|
||||||
(r'/ws', HTTPHandlerPage, handler_args),
|
(r"/api/stats/node", HTTPHandlerPage, handler_args),
|
||||||
(r'/upload', HTTPHandlerPage, handler_args)]
|
(r"/ws", HTTPHandlerPage, handler_args),
|
||||||
HTTPapp = tornado.web.Application(
|
(r"/upload", HTTPHandlerPage, handler_args),
|
||||||
HTTPhanders,
|
]
|
||||||
template_path=os.path.join(helper.webroot, 'templates'),
|
http_app = tornado.web.Application(
|
||||||
static_path=os.path.join(helper.webroot, 'static'),
|
http_handers,
|
||||||
|
template_path=os.path.join(self.helper.webroot, "templates"),
|
||||||
|
static_path=os.path.join(self.helper.webroot, "static"),
|
||||||
debug=debug_errors,
|
debug=debug_errors,
|
||||||
cookie_secret=cookie_secret,
|
cookie_secret=cookie_secret,
|
||||||
xsrf_cookies=True,
|
xsrf_cookies=True,
|
||||||
autoreload=False,
|
autoreload=False,
|
||||||
log_function=self.log_function,
|
log_function=self.log_function,
|
||||||
default_handler_class = HTTPHandler,
|
default_handler_class=HTTPHandler,
|
||||||
login_url="/login",
|
login_url="/login",
|
||||||
serve_traceback=debug_errors,
|
serve_traceback=debug_errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.HTTP_Server = tornado.httpserver.HTTPServer(HTTPapp)
|
self.http_server = tornado.httpserver.HTTPServer(http_app)
|
||||||
self.HTTP_Server.listen(http_port)
|
self.http_server.listen(http_port)
|
||||||
|
|
||||||
self.HTTPS_Server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
|
self.https_server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
|
||||||
self.HTTPS_Server.listen(https_port)
|
self.https_server.listen(https_port)
|
||||||
|
|
||||||
logger.info(f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections.")
|
logger.info(
|
||||||
console.info(f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections.")
|
f"https://{Helpers.get_local_ip()}:{https_port} "
|
||||||
|
f"is up and ready for connections."
|
||||||
|
)
|
||||||
|
Console.info(
|
||||||
|
f"https://{Helpers.get_local_ip()}:{https_port} "
|
||||||
|
f"is up and ready for connections."
|
||||||
|
)
|
||||||
|
|
||||||
console.info("Server Init Complete: Listening For Connections:")
|
Console.info("Server Init Complete: Listening For Connections:")
|
||||||
|
|
||||||
self.ioloop = tornado.ioloop.IOLoop.current()
|
self.ioloop = tornado.ioloop.IOLoop.current()
|
||||||
self.ioloop.start()
|
self.ioloop.start()
|
||||||
|
|
||||||
def stop_web_server(self):
|
def stop_web_server(self):
|
||||||
logger.info("Shutting Down Web Server")
|
logger.info("Shutting Down Web Server")
|
||||||
console.info("Shutting Down Web Server")
|
Console.info("Shutting Down Web Server")
|
||||||
self.ioloop.stop()
|
self.ioloop.stop()
|
||||||
self.HTTP_Server.stop()
|
self.http_server.stop()
|
||||||
self.HTTPS_Server.stop()
|
self.https_server.stop()
|
||||||
logger.info("Web Server Stopped")
|
logger.info("Web Server Stopped")
|
||||||
console.info("Web Server Stopped")
|
Console.info("Web Server Stopped")
|
||||||
|
@ -1,79 +1,133 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import tornado.web
|
||||||
|
import tornado.options
|
||||||
|
import tornado.httpserver
|
||||||
|
|
||||||
from app.classes.models.server_permissions import Enum_Permissions_Server
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
from app.classes.shared.helpers import helper
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.main_controller import Controller
|
from app.classes.shared.main_controller import Controller
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
|
|
||||||
try:
|
|
||||||
import tornado.web
|
|
||||||
import tornado.options
|
|
||||||
import tornado.httpserver
|
|
||||||
|
|
||||||
except ModuleNotFoundError as ex:
|
|
||||||
helper.auto_installer_fix(ex)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Class & Function Defination
|
|
||||||
MAX_STREAMED_SIZE = 1024 * 1024 * 1024
|
|
||||||
|
|
||||||
@tornado.web.stream_request_body
|
@tornado.web.stream_request_body
|
||||||
class UploadHandler(BaseHandler):
|
class UploadHandler(BaseHandler):
|
||||||
|
|
||||||
# noinspection PyAttributeOutsideInit
|
# noinspection PyAttributeOutsideInit
|
||||||
def initialize(self, controller: Controller=None, tasks_manager=None, translator=None):
|
def initialize(
|
||||||
|
self,
|
||||||
|
helper: Helpers = None,
|
||||||
|
controller: Controller = None,
|
||||||
|
tasks_manager=None,
|
||||||
|
translator=None,
|
||||||
|
):
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.tasks_manager = tasks_manager
|
self.tasks_manager = tasks_manager
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
self.do_upload = True
|
# Class & Function Defination
|
||||||
# pylint: disable=unused-variable
|
api_key, _token_data, exec_user = self.current_user
|
||||||
api_key, token_data, exec_user = self.current_user
|
server_id = self.get_argument("server_id", None)
|
||||||
server_id = self.get_argument('server_id', None)
|
superuser = exec_user["superuser"]
|
||||||
superuser = exec_user['superuser']
|
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
superuser = superuser and api_key.superuser
|
superuser = superuser and api_key.superuser
|
||||||
user_id = exec_user['user_id']
|
user_id = exec_user["user_id"]
|
||||||
|
stream_size_value = self.helper.get_setting("stream_size_GB")
|
||||||
|
|
||||||
|
max_streamed_size = (1024 * 1024 * 1024) * stream_size_value
|
||||||
|
|
||||||
|
self.content_len = int(self.request.headers.get("Content-Length"))
|
||||||
|
if self.content_len > max_streamed_size:
|
||||||
|
logger.error(
|
||||||
|
f"User with ID {user_id} attempted to upload a file that"
|
||||||
|
f" exceeded the max body size."
|
||||||
|
)
|
||||||
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
user_id,
|
||||||
|
"send_start_error",
|
||||||
|
{
|
||||||
|
"error": self.helper.translation.translate(
|
||||||
|
"error",
|
||||||
|
"fileTooLarge",
|
||||||
|
self.controller.users.get_user_lang_by_id(user_id),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
self.do_upload = True
|
||||||
|
|
||||||
if superuser:
|
if superuser:
|
||||||
exec_user_server_permissions = self.controller.server_perms.list_defined_permissions()
|
exec_user_server_permissions = (
|
||||||
|
self.controller.server_perms.list_defined_permissions()
|
||||||
|
)
|
||||||
elif api_key is not None:
|
elif api_key is not None:
|
||||||
exec_user_server_permissions = self.controller.server_perms.get_api_key_permissions_list(api_key, server_id)
|
exec_user_server_permissions = (
|
||||||
|
self.controller.server_perms.get_api_key_permissions_list(
|
||||||
|
api_key, server_id
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
exec_user_server_permissions = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user_server_permissions = (
|
||||||
exec_user["user_id"], server_id)
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
|
exec_user["user_id"], server_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
server_id = self.request.headers.get('X-ServerId', None)
|
server_id = self.request.headers.get("X-ServerId", None)
|
||||||
|
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
logger.warning('User ID not found in upload handler call')
|
logger.warning("User ID not found in upload handler call")
|
||||||
console.warning('User ID not found in upload handler call')
|
Console.warning("User ID not found in upload handler call")
|
||||||
self.do_upload = False
|
self.do_upload = False
|
||||||
|
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning('Server ID not found in upload handler call')
|
logger.warning("Server ID not found in upload handler call")
|
||||||
console.warning('Server ID not found in upload handler call')
|
Console.warning("Server ID not found in upload handler call")
|
||||||
self.do_upload = False
|
self.do_upload = False
|
||||||
|
|
||||||
if Enum_Permissions_Server.Files not in exec_user_server_permissions:
|
if EnumPermissionsServer.FILES not in exec_user_server_permissions:
|
||||||
logger.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
logger.warning(
|
||||||
console.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
f"User {user_id} tried to upload a file to "
|
||||||
|
f"{server_id} without permissions!"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"User {user_id} tried to upload a file to "
|
||||||
|
f"{server_id} without permissions!"
|
||||||
|
)
|
||||||
self.do_upload = False
|
self.do_upload = False
|
||||||
|
|
||||||
path = self.request.headers.get('X-Path', None)
|
path = self.request.headers.get("X-Path", None)
|
||||||
filename = self.request.headers.get('X-FileName', None)
|
filename = self.request.headers.get("X-FileName", None)
|
||||||
full_path = os.path.join(path, filename)
|
full_path = os.path.join(path, filename)
|
||||||
|
|
||||||
if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), full_path):
|
if not Helpers.in_path(
|
||||||
print(user_id, server_id, helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), full_path)
|
Helpers.get_os_understandable_path(
|
||||||
logger.warning(f'User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!')
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
console.warning(f'User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!')
|
),
|
||||||
|
full_path,
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
user_id,
|
||||||
|
server_id,
|
||||||
|
Helpers.get_os_understandable_path(
|
||||||
|
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||||
|
),
|
||||||
|
full_path,
|
||||||
|
)
|
||||||
|
logger.warning(
|
||||||
|
f"User {user_id} tried to upload a file to {server_id} "
|
||||||
|
f"but the path is not inside of the server!"
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
f"User {user_id} tried to upload a file to {server_id} "
|
||||||
|
f"but the path is not inside of the server!"
|
||||||
|
)
|
||||||
self.do_upload = False
|
self.do_upload = False
|
||||||
|
|
||||||
if self.do_upload:
|
if self.do_upload:
|
||||||
@ -83,23 +137,23 @@ class UploadHandler(BaseHandler):
|
|||||||
logger.error(f"Upload failed with error: {e}")
|
logger.error(f"Upload failed with error: {e}")
|
||||||
self.do_upload = False
|
self.do_upload = False
|
||||||
# If max_body_size is not set, you cannot upload files > 100MB
|
# If max_body_size is not set, you cannot upload files > 100MB
|
||||||
self.request.connection.set_max_body_size(MAX_STREAMED_SIZE)
|
self.request.connection.set_max_body_size(max_streamed_size)
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
logger.info("Upload completed")
|
logger.info("Upload completed")
|
||||||
files_left = int(self.request.headers.get('X-Files-Left', None))
|
files_left = int(self.request.headers.get("X-Files-Left", None))
|
||||||
|
|
||||||
if self.do_upload:
|
if self.do_upload:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
if files_left == 0:
|
if files_left == 0:
|
||||||
websocket_helper.broadcast('close_upload_box', 'success')
|
self.helper.websocket_helper.broadcast("close_upload_box", "success")
|
||||||
self.finish('success') # Nope, I'm sending "success"
|
self.finish("success") # Nope, I'm sending "success"
|
||||||
self.f.close()
|
self.f.close()
|
||||||
else:
|
else:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
if files_left == 0:
|
if files_left == 0:
|
||||||
websocket_helper.broadcast('close_upload_box', 'error')
|
self.helper.websocket_helper.broadcast("close_upload_box", "error")
|
||||||
self.finish('error')
|
self.finish("error")
|
||||||
|
|
||||||
def data_received(self, chunk):
|
def data_received(self, chunk):
|
||||||
if self.do_upload:
|
if self.do_upload:
|
||||||
|
@ -2,19 +2,13 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from urllib.parse import parse_qsl
|
from urllib.parse import parse_qsl
|
||||||
|
import tornado.websocket
|
||||||
|
|
||||||
from app.classes.shared.authentication import authentication
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.shared.helpers import helper
|
|
||||||
from app.classes.web.websocket_helper import websocket_helper
|
|
||||||
|
|
||||||
try:
|
|
||||||
import tornado.websocket
|
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
|
||||||
helper.auto_installer_fix(e)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SocketHandler(tornado.websocket.WebSocketHandler):
|
class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
page = None
|
page = None
|
||||||
page_query_params = None
|
page_query_params = None
|
||||||
@ -23,63 +17,81 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
|||||||
translator = None
|
translator = None
|
||||||
io_loop = None
|
io_loop = None
|
||||||
|
|
||||||
def initialize(self, controller=None, tasks_manager=None, translator=None):
|
def initialize(
|
||||||
|
self, helper=None, controller=None, tasks_manager=None, translator=None
|
||||||
|
):
|
||||||
|
self.helper = helper
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.tasks_manager = tasks_manager
|
self.tasks_manager = tasks_manager
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
self.io_loop = tornado.ioloop.IOLoop.current()
|
self.io_loop = tornado.ioloop.IOLoop.current()
|
||||||
|
|
||||||
def get_remote_ip(self):
|
def get_remote_ip(self):
|
||||||
remote_ip = self.request.headers.get("X-Real-IP") or \
|
remote_ip = (
|
||||||
self.request.headers.get("X-Forwarded-For") or \
|
self.request.headers.get("X-Real-IP")
|
||||||
self.request.remote_ip
|
or self.request.headers.get("X-Forwarded-For")
|
||||||
|
or self.request.remote_ip
|
||||||
|
)
|
||||||
return remote_ip
|
return remote_ip
|
||||||
|
|
||||||
def get_user_id(self):
|
def get_user_id(self):
|
||||||
_, _, user = authentication.check(self.get_cookie('token'))
|
_, _, user = self.controller.authentication.check(self.get_cookie("token"))
|
||||||
return user['user_id']
|
return user["user_id"]
|
||||||
|
|
||||||
def check_auth(self):
|
def check_auth(self):
|
||||||
return authentication.check_bool(self.get_cookie('token'))
|
return self.controller.authentication.check_bool(self.get_cookie("token"))
|
||||||
|
|
||||||
# pylint: disable=arguments-differ
|
# pylint: disable=arguments-differ
|
||||||
def open(self):
|
def open(self):
|
||||||
logger.debug('Checking WebSocket authentication')
|
logger.debug("Checking WebSocket authentication")
|
||||||
if self.check_auth():
|
if self.check_auth():
|
||||||
self.handle()
|
self.handle()
|
||||||
else:
|
else:
|
||||||
websocket_helper.send_message(self, 'notification', 'Not authenticated for WebSocket connection')
|
self.helper.websocket_helper.send_message(
|
||||||
|
self, "notification", "Not authenticated for WebSocket connection"
|
||||||
|
)
|
||||||
self.close()
|
self.close()
|
||||||
self.controller.management.add_to_audit_log_raw('unknown',
|
self.controller.management.add_to_audit_log_raw(
|
||||||
0, 0,
|
"unknown",
|
||||||
'Someone tried to connect via WebSocket without proper authentication',
|
0,
|
||||||
self.get_remote_ip())
|
0,
|
||||||
websocket_helper.broadcast('notification', 'Someone tried to connect via WebSocket without proper authentication')
|
"Someone tried to connect via WebSocket without proper authentication",
|
||||||
logger.warning('Someone tried to connect via WebSocket without proper authentication')
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
self.helper.websocket_helper.broadcast(
|
||||||
|
"notification",
|
||||||
|
"Someone tried to connect via WebSocket without proper authentication",
|
||||||
|
)
|
||||||
|
logger.warning(
|
||||||
|
"Someone tried to connect via WebSocket without proper authentication"
|
||||||
|
)
|
||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
self.page = self.get_query_argument('page')
|
self.page = self.get_query_argument("page")
|
||||||
self.page_query_params = dict(parse_qsl(helper.remove_prefix(
|
self.page_query_params = dict(
|
||||||
self.get_query_argument('page_query_params'),
|
parse_qsl(
|
||||||
'?'
|
Helpers.remove_prefix(self.get_query_argument("page_query_params"), "?")
|
||||||
)))
|
)
|
||||||
websocket_helper.add_client(self)
|
)
|
||||||
logger.debug('Opened WebSocket connection')
|
self.helper.websocket_helper.add_client(self)
|
||||||
|
logger.debug("Opened WebSocket connection")
|
||||||
|
|
||||||
# pylint: disable=arguments-renamed
|
# pylint: disable=arguments-renamed
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def on_message(raw_message):
|
def on_message(raw_message):
|
||||||
|
|
||||||
logger.debug(f'Got message from WebSocket connection {raw_message}')
|
logger.debug(f"Got message from WebSocket connection {raw_message}")
|
||||||
message = json.loads(raw_message)
|
message = json.loads(raw_message)
|
||||||
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
|
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
websocket_helper.remove_client(self)
|
self.helper.websocket_helper.remove_client(self)
|
||||||
logger.debug('Closed WebSocket connection')
|
logger.debug("Closed WebSocket connection")
|
||||||
|
|
||||||
async def write_message_int(self, message):
|
async def write_message_int(self, message):
|
||||||
self.write_message(message)
|
self.write_message(message)
|
||||||
|
|
||||||
def write_message_helper(self, message):
|
def write_message_helper(self, message):
|
||||||
asyncio.run_coroutine_threadsafe(self.write_message_int(message), self.io_loop.asyncio_loop)
|
asyncio.run_coroutine_threadsafe(
|
||||||
|
self.write_message_int(message), self.io_loop.asyncio_loop
|
||||||
|
)
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import Console
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class WebSocketHelper:
|
class WebSocketHelper:
|
||||||
def __init__(self):
|
def __init__(self, helper):
|
||||||
|
self.helper = helper
|
||||||
self.clients = set()
|
self.clients = set()
|
||||||
|
|
||||||
def add_client(self, client):
|
def add_client(self, client):
|
||||||
@ -15,19 +17,26 @@ class WebSocketHelper:
|
|||||||
def remove_client(self, client):
|
def remove_client(self, client):
|
||||||
self.clients.remove(client)
|
self.clients.remove(client)
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
def send_message(
|
||||||
def send_message(self, client, event_type: str, data):
|
self, client, event_type: str, data
|
||||||
|
): # pylint: disable=no-self-use
|
||||||
if client.check_auth():
|
if client.check_auth():
|
||||||
message = str(json.dumps({'event': event_type, 'data': data}))
|
message = str(json.dumps({"event": event_type, "data": data}))
|
||||||
client.write_message_helper(message)
|
client.write_message_helper(message)
|
||||||
|
|
||||||
def broadcast(self, event_type: str, data):
|
def broadcast(self, event_type: str, data):
|
||||||
logger.debug(f"Sending to {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}")
|
logger.debug(
|
||||||
|
f"Sending to {len(self.clients)} clients: "
|
||||||
|
f"{json.dumps({'event': event_type, 'data': data})}"
|
||||||
|
)
|
||||||
for client in self.clients:
|
for client in self.clients:
|
||||||
try:
|
try:
|
||||||
self.send_message(client, event_type, data)
|
self.send_message(client, event_type, data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f'Error caught while sending WebSocket message to {client.get_remote_ip()} {e}')
|
logger.exception(
|
||||||
|
f"Error caught while sending WebSocket message to "
|
||||||
|
f"{client.get_remote_ip()} {e}"
|
||||||
|
)
|
||||||
|
|
||||||
def broadcast_page(self, page: str, event_type: str, data):
|
def broadcast_page(self, page: str, event_type: str, data):
|
||||||
def filter_fn(client):
|
def filter_fn(client):
|
||||||
@ -51,7 +60,9 @@ class WebSocketHelper:
|
|||||||
|
|
||||||
self.broadcast_with_fn(filter_fn, event_type, data)
|
self.broadcast_with_fn(filter_fn, event_type, data)
|
||||||
|
|
||||||
def broadcast_user_page_params(self, page: str, params: dict, user_id: str, event_type: str, data):
|
def broadcast_user_page_params(
|
||||||
|
self, page: str, params: dict, user_id: str, event_type: str, data
|
||||||
|
):
|
||||||
def filter_fn(client):
|
def filter_fn(client):
|
||||||
if client.get_user_id() != user_id:
|
if client.get_user_id() != user_id:
|
||||||
return False
|
return False
|
||||||
@ -77,18 +88,22 @@ class WebSocketHelper:
|
|||||||
|
|
||||||
def broadcast_with_fn(self, filter_fn, event_type: str, data):
|
def broadcast_with_fn(self, filter_fn, event_type: str, data):
|
||||||
clients = list(filter(filter_fn, self.clients))
|
clients = list(filter(filter_fn, self.clients))
|
||||||
logger.debug(f"Sending to {len(clients)} out of {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}")
|
logger.debug(
|
||||||
|
f"Sending to {len(clients)} out of {len(self.clients)} "
|
||||||
|
f"clients: {json.dumps({'event': event_type, 'data': data})}"
|
||||||
|
)
|
||||||
|
|
||||||
for client in clients:
|
for client in clients:
|
||||||
try:
|
try:
|
||||||
self.send_message(client, event_type, data)
|
self.send_message(client, event_type, data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f'Error catched while sending WebSocket message to {client.get_remote_ip()} {e}')
|
logger.exception(
|
||||||
|
f"Error catched while sending WebSocket message to "
|
||||||
|
f"{client.get_remote_ip()} {e}"
|
||||||
|
)
|
||||||
|
|
||||||
def disconnect_all(self):
|
def disconnect_all(self):
|
||||||
console.info('Disconnecting WebSocket clients')
|
Console.info("Disconnecting WebSocket clients")
|
||||||
for client in self.clients:
|
for client in self.clients:
|
||||||
client.close()
|
client.close()
|
||||||
console.info('Disconnected WebSocket clients')
|
Console.info("Disconnected WebSocket clients")
|
||||||
|
|
||||||
websocket_helper = WebSocketHelper()
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"https": true,
|
|
||||||
"http_port": 8000,
|
"http_port": 8000,
|
||||||
"https_port": 8443,
|
"https_port": 8443,
|
||||||
"language": "en_EN",
|
"language": "en_EN",
|
||||||
@ -14,7 +13,15 @@
|
|||||||
"virtual_terminal_lines": 70,
|
"virtual_terminal_lines": 70,
|
||||||
"max_log_lines": 700,
|
"max_log_lines": 700,
|
||||||
"max_audit_entries": 300,
|
"max_audit_entries": 300,
|
||||||
"disabled_language_files": ["lol_EN.json", ""],
|
"disabled_language_files": [
|
||||||
"keywords": ["help", "chunk"],
|
"lol_EN.json",
|
||||||
"allow_nsfw_profile_pictures": false
|
""
|
||||||
|
],
|
||||||
|
"stream_size_GB": 1,
|
||||||
|
"keywords": [
|
||||||
|
"help",
|
||||||
|
"chunk"
|
||||||
|
],
|
||||||
|
"allow_nsfw_profile_pictures": false,
|
||||||
|
"enable_user_self_delete": false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"major": 4,
|
"major": 4,
|
||||||
"minor": 0,
|
"minor": 0,
|
||||||
"sub": 0,
|
"sub": 0,
|
||||||
"meta": "alpha.3.5"
|
"meta": "beta"
|
||||||
}
|
}
|
178
app/frontend/static/assets/css/jquery-ui.css
vendored
Normal file
178
app/frontend/static/assets/css/jquery-ui.css
vendored
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*! jQuery UI - v1.13.1 - 2022-03-14
|
||||||
|
* http://jqueryui.com
|
||||||
|
* Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css
|
||||||
|
* Copyright jQuery Foundation and other contributors; Licensed MIT */
|
||||||
|
|
||||||
|
.ui-draggable-handle {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
/* Layout helpers
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-helper-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ui-helper-hidden-accessible {
|
||||||
|
border: 0;
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
.ui-helper-reset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 100%;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.ui-helper-clearfix:before,
|
||||||
|
.ui-helper-clearfix:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.ui-helper-clearfix:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.ui-helper-zfix {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-front {
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interaction Cues
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-state-disabled {
|
||||||
|
cursor: default !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Icons
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-top: -.25em;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -99999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-widget-icon-block {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc visuals
|
||||||
|
----------------------------------*/
|
||||||
|
|
||||||
|
/* Overlays */
|
||||||
|
.ui-widget-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.ui-resizable-handle {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 0.1px;
|
||||||
|
display: block;
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.ui-resizable-disabled .ui-resizable-handle,
|
||||||
|
.ui-resizable-autohide .ui-resizable-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ui-resizable-n {
|
||||||
|
cursor: n-resize;
|
||||||
|
height: 7px;
|
||||||
|
width: 100%;
|
||||||
|
top: -5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.ui-resizable-s {
|
||||||
|
cursor: s-resize;
|
||||||
|
height: 7px;
|
||||||
|
width: 100%;
|
||||||
|
bottom: -5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.ui-resizable-e {
|
||||||
|
cursor: e-resize;
|
||||||
|
width: 7px;
|
||||||
|
right: -5px;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable-w {
|
||||||
|
cursor: w-resize;
|
||||||
|
width: 7px;
|
||||||
|
left: -5px;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable-se {
|
||||||
|
cursor: se-resize;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
right: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
.ui-resizable-sw {
|
||||||
|
cursor: sw-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
left: -5px;
|
||||||
|
bottom: -5px;
|
||||||
|
}
|
||||||
|
.ui-resizable-nw {
|
||||||
|
cursor: nw-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
left: -5px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
.ui-resizable-ne {
|
||||||
|
cursor: ne-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
right: -5px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
.ui-selectable {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.ui-selectable-helper {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
border: 1px dotted black;
|
||||||
|
}
|
||||||
|
.ui-sortable-handle {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
173
app/frontend/static/assets/css/jquery-ui.structure.css
vendored
Normal file
173
app/frontend/static/assets/css/jquery-ui.structure.css
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
.ui-draggable-handle {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
/* Layout helpers
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-helper-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ui-helper-hidden-accessible {
|
||||||
|
border: 0;
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
.ui-helper-reset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 100%;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.ui-helper-clearfix:before,
|
||||||
|
.ui-helper-clearfix:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.ui-helper-clearfix:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.ui-helper-zfix {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-front {
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interaction Cues
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-state-disabled {
|
||||||
|
cursor: default !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Icons
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-top: -.25em;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -99999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-widget-icon-block {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc visuals
|
||||||
|
----------------------------------*/
|
||||||
|
|
||||||
|
/* Overlays */
|
||||||
|
.ui-widget-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.ui-resizable-handle {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 0.1px;
|
||||||
|
display: block;
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.ui-resizable-disabled .ui-resizable-handle,
|
||||||
|
.ui-resizable-autohide .ui-resizable-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ui-resizable-n {
|
||||||
|
cursor: n-resize;
|
||||||
|
height: 7px;
|
||||||
|
width: 100%;
|
||||||
|
top: -5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.ui-resizable-s {
|
||||||
|
cursor: s-resize;
|
||||||
|
height: 7px;
|
||||||
|
width: 100%;
|
||||||
|
bottom: -5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.ui-resizable-e {
|
||||||
|
cursor: e-resize;
|
||||||
|
width: 7px;
|
||||||
|
right: -5px;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable-w {
|
||||||
|
cursor: w-resize;
|
||||||
|
width: 7px;
|
||||||
|
left: -5px;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ui-resizable-se {
|
||||||
|
cursor: se-resize;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
right: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
.ui-resizable-sw {
|
||||||
|
cursor: sw-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
left: -5px;
|
||||||
|
bottom: -5px;
|
||||||
|
}
|
||||||
|
.ui-resizable-nw {
|
||||||
|
cursor: nw-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
left: -5px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
.ui-resizable-ne {
|
||||||
|
cursor: ne-resize;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
right: -5px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
.ui-selectable {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.ui-selectable-helper {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
border: 1px dotted black;
|
||||||
|
}
|
||||||
|
.ui-sortable-handle {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
9447
app/frontend/static/assets/js/jquery-ui.js
vendored
Normal file
9447
app/frontend/static/assets/js/jquery-ui.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -214,9 +214,14 @@
|
|||||||
};
|
};
|
||||||
wsInternal.onerror = function (errorEvent) {
|
wsInternal.onerror = function (errorEvent) {
|
||||||
console.error('WebSocket Error', errorEvent);
|
console.error('WebSocket Error', errorEvent);
|
||||||
|
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy? See our'+
|
||||||
|
' documentation for details')
|
||||||
|
|
||||||
};
|
};
|
||||||
wsInternal.onclose = function (closeEvent) {
|
wsInternal.onclose = function (closeEvent) {
|
||||||
console.log('Closed WebSocket', closeEvent);
|
console.log('Closed WebSocket', closeEvent);
|
||||||
|
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy? See our'+
|
||||||
|
' documentation for details')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -262,6 +267,17 @@
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (webSocket) {
|
||||||
|
webSocket.on('support_status_update', function (logs) {
|
||||||
|
if(logs.percent >= 100){
|
||||||
|
document.getElementById('logs_progress_bar').innerHTML = '100%';
|
||||||
|
document.getElementById('logs_progress_bar').style.width = '100%';
|
||||||
|
}else{
|
||||||
|
document.getElementById('logs_progress_bar').innerHTML = logs.percent +'%';
|
||||||
|
document.getElementById('logs_progress_bar').style.width = logs.percent + '%';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if (webSocket) {
|
if (webSocket) {
|
||||||
webSocket.on('send_logs_bootbox', function (server_id) {
|
webSocket.on('send_logs_bootbox', function (server_id) {
|
||||||
var x = document.querySelector('.bootbox');
|
var x = document.querySelector('.bootbox');
|
||||||
|
@ -32,7 +32,14 @@
|
|||||||
{% end %}
|
{% end %}
|
||||||
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{% if data['user_data']['preparing'] %}
|
||||||
|
<span class="dropdown-item" id="support_progress"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}<br><br></span>
|
||||||
|
<span class="dropdown-item" id="support_progress"><div class="support_progress" style="height: 15px; width: 100%;">
|
||||||
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar" style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||||
|
</div></span>
|
||||||
|
{% else %}
|
||||||
<a class="dropdown-item" id="support_logs" ><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
|
<a class="dropdown-item" id="support_logs" ><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
|
||||||
|
{% end %}
|
||||||
{% if data['superuser'] %}
|
{% if data['superuser'] %}
|
||||||
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
|
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
@ -24,35 +24,39 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-history"></i> Audit Logs</h4>
|
<h4 class="card-title"><i class="fas fa-history"></i> Audit Logs</h4>
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
{% if data['user_data']['hints'] %}
|
||||||
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
||||||
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
|
data-placement="top"></span>
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover" id="audit_table" style="overflow: scroll;" width="100%">
|
<table class="table table-hover" id="audit_table" style="overflow: scroll;" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="rounded">
|
<tr class="rounded">
|
||||||
<td>Username</td>
|
<td>Username</td>
|
||||||
<td>Time</td>
|
<td>Time</td>
|
||||||
<td>Action</td>
|
<td>Action</td>
|
||||||
<td>Server ID</td>
|
<td>Server ID</td>
|
||||||
<td>IP</td>
|
<td>IP</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for row in data['audit_logs'] %}
|
{% for row in data['audit_logs'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ row['user_name'] }}</td>
|
<td>{{ row['user_name'] }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
|
{{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ row['log_msg'] }}</td>
|
<td>{{ row['log_msg'] }}</td>
|
||||||
<td>{{ row['server_id'] }}</td>
|
<td>{{ row['server_id'] }}</td>
|
||||||
<td>{{ row['source_ip'] }}</td>
|
<td>{{ row['source_ip'] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,9 +64,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
.popover-body{
|
.popover-body {
|
||||||
color: white !important;;
|
color: white !important;
|
||||||
}
|
;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@ -76,37 +81,37 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log('ready for JS!')
|
console.log('ready for JS!')
|
||||||
$('#audit_table').DataTable({
|
$('#audit_table').DataTable({
|
||||||
'order': [1, 'desc']
|
'order': [1, 'desc']
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
$('[data-toggle="popover"]').popover();
|
$('[data-toggle="popover"]').popover();
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
$(window).ready(function(){
|
$(window).ready(function () {
|
||||||
$('body').click(function(){
|
$('body').click(function () {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(window).resize(function() {
|
$(window).resize(function () {
|
||||||
// This will execute whenever the window is resized
|
// This will execute whenever the window is resized
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
} // New width
|
} // New width
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
@ -15,8 +15,9 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h4 class="page-title">{{ translate('dashboard', 'dashboard', data['lang']) }}
|
<h4 class="page-title">{{ translate('dashboard', 'dashboard', data['lang']) }}
|
||||||
{% if data['server_stats']['running'] != 0 %}
|
{% if data['server_stats']['running'] != 0 %}
|
||||||
<span id="sync" style="margin-left: 5px;"><i class="fas fa-sync fa-spin"></i></span></h4>
|
<span id="sync" style="margin-left: 5px;"><i class="fas fa-sync fa-spin"></i></span>
|
||||||
{% end %}
|
</h4>
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -38,12 +39,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="wrapper my-auto ml-auto ml-lg-4">
|
<div class="wrapper my-auto ml-auto ml-lg-4">
|
||||||
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true"
|
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true"
|
||||||
title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}">
|
title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}">
|
||||||
{{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{
|
{{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{
|
||||||
data.get('hosts_data').get('cpu_usage') }}</span>
|
data.get('hosts_data').get('cpu_usage') }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top"
|
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top"
|
||||||
title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}">
|
title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}">
|
||||||
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{
|
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{
|
||||||
data.get('hosts_data').get('mem_percent') }}%</span>
|
data.get('hosts_data').get('mem_percent') }}%</span>
|
||||||
</p>
|
</p>
|
||||||
@ -74,7 +75,8 @@
|
|||||||
<h3 class="mb-0 font-weight-semibold" id="total_players">{{ data['num_players'] }}</h3>
|
<h3 class="mb-0 font-weight-semibold" id="total_players">{{ data['num_players'] }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper my-auto ml-auto ml-lg-4">
|
<div class="wrapper my-auto ml-auto ml-lg-4">
|
||||||
<p class="mb-0 text-warning"><span id="max_players">0</span> {{ translate('dashboard', 'max', data['lang']) }}</p>
|
<p class="mb-0 text-warning"><span id="max_players">0</span> {{ translate('dashboard', 'max',
|
||||||
|
data['lang']) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,9 +93,11 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('dashboard', 'allServers',
|
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('dashboard', 'allServers',
|
||||||
data['lang']) }}</h4>
|
data['lang']) }}</h4>
|
||||||
{% if len(data['servers']) > 0 %}
|
{% if len(data['servers']) > 0 %}
|
||||||
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
||||||
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
data-placement="top"></span>
|
data-placement="top"></span>
|
||||||
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{
|
||||||
translate('dashboard', 'newServer', data['lang']) }}</a></div>
|
translate('dashboard', 'newServer', data['lang']) }}</a></div>
|
||||||
@ -111,21 +115,22 @@
|
|||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if len(data['servers']) > 0 %}
|
{% if len(data['servers']) > 0 %}
|
||||||
<table class="table table-hover">
|
<!-- View for Large screen -->
|
||||||
|
<table id="servers_table" class="table table-hover d-none d-sm-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="rounded">
|
<tr class="rounded" id="first" draggable="false">
|
||||||
<th>{{ translate('dashboard', 'server', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'actions', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'actions', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'cpuUsage', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'memUsage', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'memUsage', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'size', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'size', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'players', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'players', data['lang']) }}</th>
|
||||||
<th>{{ translate('dashboard', 'status', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'status', data['lang']) }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for server in data['servers'] %}
|
{% for server in data['servers'] %}
|
||||||
<tr id="{{server['server_data']['server_id']}}" draggable="true" ondragstart="start()" ondragover="dragover()" ondragend="dragend()">
|
<tr id="{{server['server_data']['server_id']}}" draggable="true">
|
||||||
<td draggable="false">
|
<td draggable="false">
|
||||||
<i class="fas fa-server"></i>
|
<i class="fas fa-server"></i>
|
||||||
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||||
@ -133,21 +138,21 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
<td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
||||||
{% if server['user_command_permission'] %}
|
{% if server['user_command_permission'] %}
|
||||||
{% if server['stats']['running'] %}
|
{% if server['stats']['running'] %}
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="stop_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="stop_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||||
<i class="fas fa-stop"></i>
|
<i class="fas fa-stop"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="restart_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="restart_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||||
<i class="fas fa-sync"></i>
|
<i class="fas fa-sync"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
<i class="fas fa-skull"></i>
|
<i class="fas fa-skull"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@ -160,29 +165,29 @@
|
|||||||
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{
|
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{
|
||||||
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
||||||
data['lang']) }}</i></a>
|
data['lang']) }}</i></a>
|
||||||
{% elif server['stats']['downloading']%}
|
{% elif server['stats']['downloading']%}
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
|
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
|
||||||
data['lang']) }}</a>
|
data['lang']) }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="play_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||||
<i class="fas fa-play"></i>
|
<i class="fas fa-play"></i>
|
||||||
</a>
|
</a>
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||||
<i class="fas fa-clone"></i>
|
<i class="fas fa-clone"></i>
|
||||||
</a>
|
</a>
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
||||||
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
<i class="fas fa-skull"></i>
|
<i class="fas fa-skull"></i>
|
||||||
</a>
|
</a>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td id="server_cpu_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_cpu_{{server['server_data']['server_id']}}">
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
title="{{server['stats']['cpu']}}">
|
title="{{server['stats']['cpu']}}">
|
||||||
<div class="progress-bar
|
<div class="progress-bar
|
||||||
{% if server['stats']['cpu'] <= 33 %}
|
{% if server['stats']['cpu'] <= 33 %}
|
||||||
bg-success
|
bg-success
|
||||||
@ -192,14 +197,14 @@
|
|||||||
bg-danger
|
bg-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
</div>
|
</div>
|
||||||
{{server['stats']['cpu']}}%
|
{{server['stats']['cpu']}}%
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td id="server_mem_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_mem_{{server['server_data']['server_id']}}">
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
title="{{server['stats']['mem']}}">
|
title="{{server['stats']['mem']}}">
|
||||||
<div class="progress-bar
|
<div class="progress-bar
|
||||||
{% if server['stats']['mem_percent'] <= 33 %}
|
{% if server['stats']['mem_percent'] <= 33 %}
|
||||||
bg-success
|
bg-success
|
||||||
@ -209,7 +214,7 @@
|
|||||||
bg-danger
|
bg-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
</div>
|
</div>
|
||||||
{{server['stats']['mem_percent']}}% -
|
{{server['stats']['mem_percent']}}% -
|
||||||
|
|
||||||
@ -219,16 +224,18 @@
|
|||||||
{{server['stats']['mem']}}
|
{{server['stats']['mem']}}
|
||||||
{% end %}
|
{% end %}
|
||||||
</td>
|
</td>
|
||||||
<td id="server_world_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_world_{{server['server_data']['server_id']}}">
|
||||||
{{ server['stats']['world_size'] }}
|
{{ server['stats']['world_size'] }}
|
||||||
</td>
|
</td>
|
||||||
<td id="server_desc_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_desc_{{server['server_data']['server_id']}}">
|
||||||
{% if server['stats']['int_ping_results'] %}
|
{% if server['stats']['int_ping_results'] %}
|
||||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max',
|
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max',
|
||||||
data['lang']) }} <br />
|
data['lang']) }} <br />
|
||||||
|
|
||||||
{% if server['stats']['desc'] != 'False' %}
|
{% if server['stats']['desc'] != 'False' %}
|
||||||
{{ server['stats']['desc'] }} <br />
|
<div id="desc_id"
|
||||||
|
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">{{
|
||||||
|
server['stats']['desc'] }}</div> <br />
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% if server['stats']['version'] != 'False' %}
|
{% if server['stats']['version'] != 'False' %}
|
||||||
@ -237,22 +244,180 @@
|
|||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td id="server_running_status_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_running_status_{{server['server_data']['server_id']}}">
|
||||||
{% if server['stats']['running'] %}
|
{% if server['stats']['running'] %}
|
||||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% elif server['stats']['crashed'] %}
|
{% elif server['stats']['crashed'] %}
|
||||||
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed',
|
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</td>
|
</td>
|
||||||
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}" data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
|
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}"
|
||||||
|
data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!-- View for Small screen -->
|
||||||
|
<table id="servers_table" class="table table-hover d-table d-sm-none">
|
||||||
|
<thead>
|
||||||
|
<tr class="rounded" id="first" draggable="false">
|
||||||
|
<th scope="col" draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
||||||
|
<th scope="col" draggable="false">{{ translate('dashboard', 'actions', data['lang']) }}</th>
|
||||||
|
<th scope="col" draggable="false">{{ translate('dashboard', 'status', data['lang']) }}</th>
|
||||||
|
<th scope="col" draggable="false"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for server in data['servers'] %}
|
||||||
|
<tr id="{{server['server_data']['server_id']}}" draggable="false">
|
||||||
|
<td scope="row"><i class="fas fa-server"></i>
|
||||||
|
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||||
|
{{ server['server_data']['server_name'] }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
||||||
|
{% if server['user_command_permission'] %}
|
||||||
|
{% if server['stats']['running'] %}
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="stop_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||||
|
<i class="fas fa-stop"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="restart_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||||
|
<i class="fas fa-sync"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
|
<i class="fas fa-skull"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{% elif server['stats']['updating']%}
|
||||||
|
<!-- WHAT HAPPENED HERE -->
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating', data['lang']) }}</i></a>
|
||||||
|
{% elif server['stats']['waiting_start']%}
|
||||||
|
<!-- WHAT HAPPENED HERE -->
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{
|
||||||
|
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting', data['lang']) }}</i></a>
|
||||||
|
{% elif server['stats']['downloading']%}
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading', data['lang']) }}</a>
|
||||||
|
{% else %}
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="play_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</a>
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||||
|
<i class="fas fa-clone"></i>
|
||||||
|
</a>
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button"
|
||||||
|
data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
|
<i class="fas fa-skull"></i>
|
||||||
|
</a>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</td>
|
||||||
|
<td draggable="false" id="m_server_running_status_{{server['server_data']['server_id']}}">
|
||||||
|
{% if server['stats']['running'] %}
|
||||||
|
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% elif server['stats']['crashed'] %}
|
||||||
|
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% end %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span data-toggle="collapse" data-target="#details_{{server['server_data']['server_id']}}" aria-expanded="false" aria-controls="details_{{server['server_data']['server_id']}}"><i class="fas fa-chevron-down"></i></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="details_{{server['server_data']['server_id']}}" class="collapse" draggable="false">
|
||||||
|
<td colspan="4">
|
||||||
|
<div class="collapse" id="details_{{server['server_data']['server_id']}}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
||||||
|
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
||||||
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
|
title="{{server['stats']['cpu']}}">
|
||||||
|
<div class="progress-bar
|
||||||
|
{% if server['stats']['cpu'] <= 33 %}
|
||||||
|
bg-success
|
||||||
|
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
||||||
|
bg-warning
|
||||||
|
{% else %}
|
||||||
|
bg-danger
|
||||||
|
{% end %}
|
||||||
|
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||||
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
{{server['stats']['cpu']}}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
||||||
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
|
title="{{server['stats']['mem']}}">
|
||||||
|
<div class="progress-bar
|
||||||
|
{% if server['stats']['mem_percent'] <= 33 %}
|
||||||
|
bg-success
|
||||||
|
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
||||||
|
bg-warning
|
||||||
|
{% else %}
|
||||||
|
bg-danger
|
||||||
|
{% end %}
|
||||||
|
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||||
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
{{server['stats']['mem_percent']}}% -
|
||||||
|
|
||||||
|
{% if server['stats']['mem'] == 0 %}
|
||||||
|
0 MB
|
||||||
|
{% else %}
|
||||||
|
{{server['stats']['mem']}}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
||||||
|
{{ server['stats']['world_size'] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" style="width: auto;">
|
||||||
|
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
||||||
|
{% if server['stats']['int_ping_results'] %}
|
||||||
|
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max',
|
||||||
|
data['lang']) }} <br />
|
||||||
|
|
||||||
|
{% if server['stats']['desc'] != 'False' %}
|
||||||
|
<div id="desc_id" style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">{{ server['stats']['desc'] }}</div> <br />
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% if server['stats']['version'] != 'False' %}
|
||||||
|
{{ server['stats']['version'] }}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% end %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% end %}
|
{% end %}
|
||||||
@ -265,11 +430,25 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
<div id="mobile"></div>
|
||||||
<style>
|
<style>
|
||||||
.popover-body {
|
.popover-body {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#desc_id {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* for Internet Explorer, Edge */
|
||||||
|
scrollbar-width: none;
|
||||||
|
/* for Firefox */
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#desc_id::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
/* for Chrome, Safari, and Opera */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@ -280,7 +459,7 @@
|
|||||||
<script src="/static/assets/js/motd.js"></script>
|
<script src="/static/assets/js/motd.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function display_motd() {
|
function display_motd() {
|
||||||
var all_motds = Array.from(document.getElementsByClassName('input_motd'));
|
let all_motds = Array.from(document.getElementsByClassName('input_motd'));
|
||||||
for (element of all_motds) {
|
for (element of all_motds) {
|
||||||
initParser(element.id, element.id);
|
initParser(element.id, element.id);
|
||||||
};
|
};
|
||||||
@ -291,13 +470,14 @@
|
|||||||
if ($(window).width() < 1000) {
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).ready(function () {
|
$(window).ready(function () {
|
||||||
$('body').click(function () {
|
$('body').click(function () {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).resize(function () {
|
$(window).resize(function () {
|
||||||
// This will execute whenever the window is resized
|
// This will execute whenever the window is resized
|
||||||
if ($(window).width() < 1000) {
|
if ($(window).width() < 1000) {
|
||||||
@ -312,7 +492,7 @@
|
|||||||
|
|
||||||
function send_command(server_id, command) {
|
function send_command(server_id, command) {
|
||||||
/* this getCookie function is in base.html */
|
/* this getCookie function is in base.html */
|
||||||
var token = getCookie("_xsrf");
|
const token = getCookie("_xsrf");
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@ -332,7 +512,7 @@
|
|||||||
|
|
||||||
function send_kill(server_id) {
|
function send_kill(server_id) {
|
||||||
/* this getCookie function is in base.html */
|
/* this getCookie function is in base.html */
|
||||||
var token = getCookie("_xsrf");
|
const token = getCookie("_xsrf");
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@ -349,6 +529,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function update_one_server_status(server) {
|
function update_one_server_status(server) {
|
||||||
|
/* Mobile view update */
|
||||||
server_cpu = document.getElementById('server_cpu_' + server.id);
|
server_cpu = document.getElementById('server_cpu_' + server.id);
|
||||||
server_mem = document.getElementById('server_mem_' + server.id);
|
server_mem = document.getElementById('server_mem_' + server.id);
|
||||||
server_world = document.getElementById('server_world_' + server.id);
|
server_world = document.getElementById('server_world_' + server.id);
|
||||||
@ -357,71 +538,66 @@
|
|||||||
server_players = document.getElementById('server_players_' + server.id);
|
server_players = document.getElementById('server_players_' + server.id);
|
||||||
total_players = document.getElementById('total_players');
|
total_players = document.getElementById('total_players');
|
||||||
|
|
||||||
|
/* Mobile view update */
|
||||||
|
m_server_cpu = document.getElementById('m_server_cpu_' + server.id);
|
||||||
|
m_server_mem = document.getElementById('m_server_mem_' + server.id);
|
||||||
|
m_server_world = document.getElementById('m_server_world_' + server.id);
|
||||||
|
m_server_desc = document.getElementById('m_server_desc_' + server.id);
|
||||||
|
m_server_online_status = document.getElementById('m_server_running_status_' + server.id);
|
||||||
|
|
||||||
console.log("Received Data : " + server.id + ": " + server);
|
console.log("Received Data : " + server.id + ": " + server);
|
||||||
/* TODO Update each element */
|
/* TODO Update each element */
|
||||||
|
|
||||||
/* Update CPU */
|
/* Update CPU */
|
||||||
cpu_status = "";
|
cpu_status = "bg-danger";
|
||||||
if (server.cpu <= 33)
|
if (server.cpu <= 33) {
|
||||||
{
|
|
||||||
cpu_status = "bg-success";
|
cpu_status = "bg-success";
|
||||||
}
|
} else if (server.cpu > 33 && server.cpu <= 66) {
|
||||||
else if (server.cpu > 33 && server.cpu <= 66)
|
|
||||||
{
|
|
||||||
cpu_status = "bg-warning";
|
cpu_status = "bg-warning";
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
server_cpu.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server.cpu + `"><div class="progress-bar ` + cpu_status + `" role="progressbar" style="width: ` + server.cpu + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.cpu + `%`;
|
||||||
cpu_status = "bg-danger";
|
m_server_cpu.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server.cpu + `"><div class="progress-bar ` + cpu_status + `" role="progressbar" style="width: ` + server.cpu + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.cpu + `%`;
|
||||||
}
|
|
||||||
server_cpu.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="`+ server.cpu +`"><div class="progress-bar `+ cpu_status + `" role="progressbar" style="width: `+ server.cpu + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>`+ server.cpu +`%`;
|
|
||||||
|
|
||||||
|
|
||||||
/* Update Memory */
|
/* Update Memory */
|
||||||
mem_status = "";
|
mem_status = "bg-danger";
|
||||||
total_mem = "";
|
total_mem = "0 MB";
|
||||||
if (server.mem_percent <= 33)
|
|
||||||
{
|
if (server.mem_percent <= 33) {
|
||||||
mem_status = "bg-success";
|
mem_status = "bg-success";
|
||||||
} else if (server.mem_percent > 33 && server.mem_percent <= 66)
|
} else if (server.mem_percent > 33 && server.mem_percent <= 66) {
|
||||||
{
|
|
||||||
mem_status = "bg-warning";
|
mem_status = "bg-warning";
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
mem_status = "bg-danger";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server.mem == 0)
|
if (server.mem !== 0) {
|
||||||
{
|
|
||||||
total_mem = "0 MB";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
total_mem = server.mem;
|
total_mem = server.mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
server_mem.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="`+ server_mem +`"><div class="progress-bar `+ mem_status + `" role="progressbar" style="width: `+ server.mem_percent + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>`+ server.mem_percent +`% - ` + total_mem;
|
server_mem.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server_mem + `"><div class="progress-bar ` + mem_status + `" role="progressbar" style="width: ` + server.mem_percent + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.mem_percent + `% - ` + total_mem;
|
||||||
|
m_server_mem.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server_mem + `"><div class="progress-bar ` + mem_status + `" role="progressbar" style="width: ` + server.mem_percent + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.mem_percent + `% - ` + total_mem;
|
||||||
|
|
||||||
/* Update World Infos */
|
/* Update World Infos */
|
||||||
server_world.innerHTML = server.world_size
|
server_world.innerHTML = server.world_size
|
||||||
|
m_server_world.innerHTML = server.world_size
|
||||||
|
|
||||||
/* Update Server Infos */
|
/* Update Server Infos */
|
||||||
if (server.int_ping_results) {
|
if (server.int_ping_results) {
|
||||||
/* Update Players */
|
/* Update Players */
|
||||||
if (server.players) {
|
if (server.players) {
|
||||||
server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />`
|
server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />`
|
||||||
|
m_server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />`
|
||||||
|
|
||||||
server_players.setAttribute('data-players', server.online);
|
server_players.setAttribute('data-players', server.online);
|
||||||
server_players.setAttribute('data-max', server.max);
|
server_players.setAttribute('data-max', server.max);
|
||||||
let servers = document.getElementsByClassName("server-player-totals");
|
let servers = document.getElementsByClassName("server-player-totals");
|
||||||
let all_total_players = 0;
|
let all_total_players = 0;
|
||||||
let all_total_max_players = 0;
|
let all_total_max_players = 0;
|
||||||
for(var i = 0; i < servers.length; i++){
|
for (let i = 0; i < servers.length; i++) {
|
||||||
try{
|
try {
|
||||||
all_total_players += parseInt(servers[i].getAttribute('data-players'));
|
all_total_players += parseInt(servers[i].getAttribute('data-players'));
|
||||||
all_total_max_players += parseInt(servers[i].getAttribute('data-max'));
|
all_total_max_players += parseInt(servers[i].getAttribute('data-max'));
|
||||||
}catch{
|
} catch {
|
||||||
console.log("Player totals are not of type int");
|
console.log("Player totals are not of type int");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,40 +607,47 @@
|
|||||||
|
|
||||||
|
|
||||||
server_infos = "";
|
server_infos = "";
|
||||||
|
m_server_infos = "";
|
||||||
server_infos = server.online + " / " + server.max + " {{ translate('dashboard', 'max', data['lang']) }}<br />"
|
server_infos = server.online + " / " + server.max + " {{ translate('dashboard', 'max', data['lang']) }}<br />"
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Motd */
|
/* Update Motd */
|
||||||
var motd = "";
|
let motd = "";
|
||||||
if (server.desc) {
|
if (server.desc) {
|
||||||
motd = `<span id="input_motd_` + server.id + `" class="input_motd">` + server.desc + `</span>`;
|
motd = `<span id="input_motd_` + server.id + `" class="input_motd">` + server.desc + `</span>`;
|
||||||
server_infos = server_infos + motd + "<br />";
|
m_server_infos = server_infos + '<div id="desc_id" style="word-wrap: break-word; overflow: auto;">' + motd + '</div>' + "<br />";
|
||||||
|
server_infos = server_infos + '<div id="desc_id" style="word-wrap: break-word; max-width: 85px !important; overflow: auto;">' + motd + '</div>' + "<br />";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Version */
|
/* Version */
|
||||||
if (server.version) {
|
if (server.version) {
|
||||||
server_infos = server_infos + server.version
|
server_infos = server_infos + server.version
|
||||||
|
m_server_infos = m_server_infos + server.version
|
||||||
}
|
}
|
||||||
server_desc.innerHTML = server_infos;
|
server_desc.innerHTML = server_infos;
|
||||||
|
m_server_desc.innerHTML = m_server_infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Online Status */
|
/* Update Online Status */
|
||||||
var online_status = "";
|
let online_status = `<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])}}</span>`;
|
||||||
|
|
||||||
if (server.running) {
|
if (server.running) {
|
||||||
online_status = `<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])}}</span>`;
|
online_status = `<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])}}</span>`;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (server.crashed){
|
if (server.crashed) {
|
||||||
online_status = `<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang'])}}</span>`
|
online_status = `<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang'])}}</span>`
|
||||||
}else{
|
|
||||||
online_status = `<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])}}</span>`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server_online_status.innerHTML = online_status;
|
server_online_status.innerHTML = online_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_servers_status(data) {
|
function update_servers_status(data) {
|
||||||
update_one_server_status(data[0]);
|
try {
|
||||||
|
update_one_server_status(data[0]);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Failed to update server stats', e)
|
||||||
|
}
|
||||||
display_motd();
|
display_motd();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,7 +701,7 @@
|
|||||||
callback: function (result) {
|
callback: function (result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
send_kill(server_id);
|
send_kill(server_id);
|
||||||
var dialog = bootbox.dialog({
|
let dialog = bootbox.dialog({
|
||||||
title: '{% raw translate("dashboard", "killing", data["lang"]) %}',
|
title: '{% raw translate("dashboard", "killing", data["lang"]) %}',
|
||||||
message: '<p><i class="fa fa-spin fa-spinner"></i> Loading...</p>'
|
message: '<p><i class="fa fa-spin fa-spinner"></i> Loading...</p>'
|
||||||
});
|
});
|
||||||
@ -540,7 +723,7 @@
|
|||||||
|
|
||||||
|
|
||||||
webSocket.on('update_host_stats', function (hostStats) {
|
webSocket.on('update_host_stats', function (hostStats) {
|
||||||
var cpuDataTitle = `{% raw translate('dashboard', 'cpuCores', data['lang']) %}: ${hostStats.cpu_cores} <br /> {% raw translate("dashboard", "cpuCurFreq", data['lang']) %}: ${hostStats.cpu_cur_freq} <br /> {% raw translate("dashboard", "cpuMaxFreq", data['lang']) %}: ${hostStats.cpu_max_freq}`;
|
let cpuDataTitle = `{% raw translate('dashboard', 'cpuCores', data['lang']) %}: ${hostStats.cpu_cores} <br /> {% raw translate("dashboard", "cpuCurFreq", data['lang']) %}: ${hostStats.cpu_cur_freq} <br /> {% raw translate("dashboard", "cpuMaxFreq", data['lang']) %}: ${hostStats.cpu_max_freq}`;
|
||||||
cpu_data.setAttribute('data-original-title', cpuDataTitle);
|
cpu_data.setAttribute('data-original-title', cpuDataTitle);
|
||||||
cpu_usage.textContent = hostStats.cpu_usage;
|
cpu_usage.textContent = hostStats.cpu_usage;
|
||||||
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
||||||
@ -577,63 +760,117 @@
|
|||||||
$(".clone_button").click(function () {
|
$(".clone_button").click(function () {
|
||||||
server_id = $(this).attr("data-id");
|
server_id = $(this).attr("data-id");
|
||||||
send_command(server_id, 'clone_server');
|
send_command(server_id, 'clone_server');
|
||||||
bootbox.alert({
|
bootbox.dialog({
|
||||||
backdrop: true,
|
backdrop: true,
|
||||||
title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}',
|
title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}',
|
||||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>'
|
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>',
|
||||||
|
closeButton: false,
|
||||||
});
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
location.reload();
|
||||||
|
}, 5000)
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script src="/static/assets/js/jquery-ui.js"></script>
|
||||||
var row;
|
<link rel="stylesheet" href="/static/assets/css/jquery-ui.css">
|
||||||
|
<link rel="stylesheet" href="/static/assets/css/jquery-ui.structure.css">
|
||||||
function start(){
|
<style>
|
||||||
row = event.target;
|
@media only screen and (max-width: 760px) {
|
||||||
}
|
#mobile {
|
||||||
function dragover(){
|
display: none;
|
||||||
var e = event;
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
let children= Array.from(e.target.parentNode.parentNode.children);
|
|
||||||
|
|
||||||
if(children.indexOf(e.target.parentNode)>children.indexOf(row))
|
|
||||||
e.target.parentNode.after(row);
|
|
||||||
else
|
|
||||||
e.target.parentNode.before(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragend(){
|
|
||||||
var id_string = '';
|
|
||||||
const table = document.querySelector("table");
|
|
||||||
for (const row of table.rows) {
|
|
||||||
if (row.getAttribute('id') != null){
|
|
||||||
if (id_string != ''){
|
|
||||||
id_string += ',' + String(row.getAttribute('id'));
|
|
||||||
}else{
|
|
||||||
id_string +=String(row.getAttribute('id'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log(id_string)
|
|
||||||
sendOrder(id_string)
|
|
||||||
}
|
}
|
||||||
function sendOrder(id_string) {
|
|
||||||
var token = getCookie("_xsrf")
|
.ui-sortable-handle {
|
||||||
$.ajax({
|
cursor: default;
|
||||||
type: "POST",
|
padding: 2px;
|
||||||
headers: {'X-XSRFToken': token},
|
}
|
||||||
url: '/ajax/send_order?order='+id_string,
|
|
||||||
data: {
|
.ui-sortable-handle:hover {
|
||||||
order: id_string,
|
cursor: grab !important;
|
||||||
},
|
padding: 2px;
|
||||||
success: function(data){
|
}
|
||||||
console.log("got response:");
|
</style>
|
||||||
console.log(data);
|
<script>
|
||||||
},
|
|
||||||
|
/* Search Bar */
|
||||||
|
$(document).ready(function () {
|
||||||
|
let servers_table = $('#servers_table').DataTable({
|
||||||
|
"ordering": false, // false to disable sorting (or any other option)
|
||||||
|
"paging": false
|
||||||
});
|
});
|
||||||
}
|
document.getElementById('first').setAttribute('draggable', false);
|
||||||
|
$('.dataTables_length').addClass('bs-select');
|
||||||
|
});
|
||||||
|
/* Search Bar End */
|
||||||
|
|
||||||
|
$(document).on("mousedown keyup click", function (event) {
|
||||||
|
const value = $('.dataTables_filter input').val();
|
||||||
|
const is_mobile = $('#mobile').css('display') === 'none';
|
||||||
|
|
||||||
|
if ($("table#servers_table tbody").sortable("toArray").length > 1 && value === '' && !is_mobile) {
|
||||||
|
$("table#servers_table tbody").sortable("enable");
|
||||||
|
} else {
|
||||||
|
$("table#servers_table tbody").sortable("disable");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
function sendOrder(id_string) {
|
||||||
|
const token = getCookie("_xsrf")
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/send_order?order=' + id_string,
|
||||||
|
data: {
|
||||||
|
order: id_string,
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inits the sortable
|
||||||
|
$("table#servers_table tbody")
|
||||||
|
.sortable({
|
||||||
|
items: '> tr',
|
||||||
|
cursor: "grabbing",
|
||||||
|
axis: "y",
|
||||||
|
revert: true,
|
||||||
|
handle: "i.fas.fa-server,a",
|
||||||
|
forcePlaceholderSize: true,
|
||||||
|
placeholder: 'table-placeholder',
|
||||||
|
deactivate: function (event, ui) {
|
||||||
|
// Gets the list of ids in the server list as an array,
|
||||||
|
// and joins the elements with an [,] and sends to the server.
|
||||||
|
const ids = $("table#servers_table tbody").sortable("toArray").join();
|
||||||
|
try {
|
||||||
|
sendOrder(ids);
|
||||||
|
} catch {
|
||||||
|
console.log("Search is actively supressing order change")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Give the table an actual width so the draggable does't collapse when dragged
|
||||||
|
$("table#servers_table")
|
||||||
|
.children()
|
||||||
|
.find("td")
|
||||||
|
.each(function () {
|
||||||
|
$(this).css("width", "").css("width", $(this).outerWidth());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fixes the appearance of a scrollbar when a user tries to drag an item too low
|
||||||
|
// Disabled on mobile since sorting is disabled on mobile
|
||||||
|
if ($('#mobile').css('display') !== 'none') {
|
||||||
|
$("div#servers_table_wrapper").css({ overflow: "hidden" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<!-- TODO: Translate the following -->
|
<!-- TODO: Translate the following -->
|
||||||
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
|
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -30,11 +30,16 @@
|
|||||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang']) }}</h4>
|
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
}}</h4>
|
||||||
|
{% if data['user_data']['hints'] %}
|
||||||
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
||||||
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
|
data-placement="top"></span>
|
||||||
|
{% end %}
|
||||||
<!-- TODO: Translate the following -->
|
<!-- TODO: Translate the following -->
|
||||||
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{ translate('panelConfig', 'newUser', data['lang']) }}</a></div>
|
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{
|
||||||
|
translate('panelConfig', 'newUser', data['lang']) }}</a></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@ -50,38 +55,38 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for user in data['users'] %}
|
{% for user in data['users'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><i class="fas fa-user"></i> {{ user.username }}</td>
|
<td><i class="fas fa-user"></i> {{ user.username }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if user.enabled %}
|
{% if user.enabled %}
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
<i class="fas fa-check-square"></i> Yes
|
<i class="fas fa-check-square"></i> Yes
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-danger">
|
<span class="text-danger">
|
||||||
<i class="far fa-times-square"></i> No
|
<i class="far fa-times-square"></i> No
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
</td>
|
</td>
|
||||||
<td id="server_list_{{user.user_id}}">
|
<td id="server_list_{{user.user_id}}">
|
||||||
<ul id="{{user.user_id}}">
|
<ul id="{{user.user_id}}">
|
||||||
{% for item in data['auth-servers'][user.user_id] %}
|
{% for item in data['auth-servers'][user.user_id] %}
|
||||||
<li>{{item}}</li>
|
<li>{{item}}</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td id="role_list_{{user.user_id}}">
|
<td id="role_list_{{user.user_id}}">
|
||||||
<ul>
|
<ul>
|
||||||
{% for item in data['user-roles'][user.user_id] %}
|
{% for item in data['user-roles'][user.user_id] %}
|
||||||
<li data-toggle="tooltip" title="{{ item }}">{{item}}</li>
|
<li data-toggle="tooltip" title="{{ item }}">{{item}}</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -93,9 +98,15 @@
|
|||||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles', data['lang']) }}</h4>
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
|
||||||
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
data['lang']) }}</h4>
|
||||||
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{ translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
{% if data['user_data']['hints'] %}
|
||||||
|
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
||||||
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
|
data-placement="top"></span>
|
||||||
|
{% end %}
|
||||||
|
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{
|
||||||
|
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@ -110,7 +121,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for role in data['roles'] %}
|
{% for role in data['roles'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ role.role_name }}</td>
|
<td>{{ role.role_name }}</td>
|
||||||
<td id="role_list_{{role.role_id}}">
|
<td id="role_list_{{role.role_id}}">
|
||||||
@ -120,19 +131,20 @@
|
|||||||
{% end %}
|
{% end %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td><ul>
|
<td>
|
||||||
{% for user in data['users'] %}
|
<ul>
|
||||||
|
{% for user in data['users'] %}
|
||||||
{% for ruser in data['user-roles'][user.user_id] %}
|
{% for ruser in data['user-roles'][user.user_id] %}
|
||||||
{% if ruser == role.role_name %}
|
{% if ruser == role.role_name %}
|
||||||
<li>{{ user.username }}</li>
|
<li>{{ user.username }}</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td><a href="/panel/edit_role?id={{role.role_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
<td><a href="/panel/edit_role?id={{role.role_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -146,10 +158,12 @@
|
|||||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls', data['lang']) }}</h4>
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
|
||||||
|
data['lang']) }}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<button type="button" class="btn btn-outline-danger clear-comm">{{ translate('panelConfig', 'clearComms', data['lang']) }}</button>
|
<button type="button" class="btn btn-outline-danger clear-comm">{{ translate('panelConfig',
|
||||||
|
'clearComms', data['lang']) }}</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -165,9 +179,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.popover-body{
|
.popover-body {
|
||||||
color: white !important;;
|
color: white !important;
|
||||||
}
|
;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
@ -175,46 +190,46 @@
|
|||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
$('[data-toggle="popover"]').popover();
|
$('[data-toggle="popover"]').popover();
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
$('.too_small2').popover("show");
|
$('.too_small2').popover("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
$(window).ready(function(){
|
$(window).ready(function () {
|
||||||
$('body').click(function(){
|
$('body').click(function () {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
$('.too_small2').popover("hide");
|
$('.too_small2').popover("hide");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(window).resize(function() {
|
$(window).resize(function () {
|
||||||
// This will execute whenever the window is resized
|
// This will execute whenever the window is resized
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
} // New width
|
} // New width
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small2').popover("show");
|
$('.too_small2').popover("show");
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$('.too_small2').popover("hide");
|
$('.too_small2').popover("hide");
|
||||||
} // New width
|
} // New width
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log('ready for JS!')
|
console.log('ready for JS!')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$( ".show_button" ).click(function() {
|
$(".show_button").click(function () {
|
||||||
console.log("showing key");
|
console.log("showing key");
|
||||||
api_key = $(this).attr("data-id");
|
api_key = $(this).attr("data-id");
|
||||||
bootbox.alert({
|
bootbox.alert({
|
||||||
backdrop: true,
|
backdrop: true,
|
||||||
title: '',
|
title: '',
|
||||||
@ -225,13 +240,13 @@ $( document ).ready(function() {
|
|||||||
$('.clear-comm').click(function () {
|
$('.clear-comm').click(function () {
|
||||||
var token = getCookie("_xsrf")
|
var token = getCookie("_xsrf")
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
headers: {'X-XSRFToken': token},
|
headers: { 'X-XSRFToken': token },
|
||||||
url: '/ajax/clear_comm',
|
url: '/ajax/clear_comm',
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
@ -37,180 +37,238 @@
|
|||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=config" role="tab" aria-selected="true">
|
<a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=config" role="tab" aria-selected="true">
|
||||||
<i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a>
|
<i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a>
|
||||||
</li>
|
</li>
|
||||||
<!-- <li class="nav-item">
|
<!-- <li class="nav-item">
|
||||||
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false">
|
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false">
|
||||||
<i class="fas fa-folder-tree"></i>Other</a>
|
<i class="fas fa-folder-tree"></i>Other</a>
|
||||||
</li> -->
|
</li> -->
|
||||||
</ul>
|
</ul>
|
||||||
<div class="row">
|
<div class="">
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="">
|
||||||
{% if data['new_role'] %}
|
<form class="forms-sample" method="post" action="{{ '/panel/add_role' if data['new_role'] else '/panel/edit_role' }}">
|
||||||
<form class="forms-sample" method="post" action="/panel/add_role">
|
{% raw xsrf_form_html() %}
|
||||||
{% else %}
|
<input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
|
||||||
<form class="forms-sample" method="post" action="/panel/edit_role">
|
<input type="hidden" name="subpage" value="config">
|
||||||
{% end %}
|
|
||||||
{% raw xsrf_form_html() %}
|
|
||||||
<input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
|
|
||||||
<input type="hidden" name="subpage" value="config">
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4>
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label>
|
<label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label>
|
||||||
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
|
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
|
||||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('rolesConfig', 'roleServers', data['lang']) }} <small class="text-muted ml-1"> {{ translate('rolesConfig', 'serversDesc', data['lang']) }}</small> </h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr class="rounded">
|
|
||||||
<th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for server in data['servers_all'] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ server['server_name'] }}</td>
|
|
||||||
<td>
|
|
||||||
{% if server['server_id'] in data['role']['servers'] %}
|
|
||||||
<input type="checkbox" class="" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" checked="" value="1">
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" class="" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" value="1">
|
|
||||||
{% end %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
|
||||||
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('rolesConfig', 'rolePerms', data['lang']) }}<small class="text-muted ml-1"> - {{ translate('rolesConfig', 'permsServer', data['lang']) }} </small></h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr class="rounded">
|
|
||||||
<th>{{ translate('rolesConfig', 'permName', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('rolesConfig', 'permAccess', data['lang']) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for permission in data['permissions_all'] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ permission.name }}</td>
|
|
||||||
<td>
|
|
||||||
{% if permission in data['permissions_list'] %}
|
|
||||||
<input type="checkbox" class="" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" checked="" value="1">
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" class="" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" value="1">
|
|
||||||
{% end %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
|
|
||||||
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 col-sm-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
|
||||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<div class="table-responsive">
|
</div>
|
||||||
<table class="table table-hover">
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('rolesConfig', 'roleServers', data['lang']) }} <small class="text-muted ml-1"> {{ translate('rolesConfig', 'serversDesc', data['lang']) }}</small> </h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="table-responsive rotate-table-parent">
|
||||||
|
<table class="table table-hover rotate-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
<style>
|
||||||
|
.rotate-table-parent {
|
||||||
|
padding-top: 2.5rem;
|
||||||
|
padding-right: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://css-tricks.com/rotated-table-column-headers-now-with-fewer-magic-numbers/ */
|
||||||
|
table.rotate-table {
|
||||||
|
--table-border-width: 1px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th.rotate-column-header {
|
||||||
|
/* Something you can count on */
|
||||||
|
height: 140px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.rotate-column-header > div {
|
||||||
|
transform:
|
||||||
|
/* Magic Numbers */
|
||||||
|
translate(0px, 51px)
|
||||||
|
/* 315 is 360 - 45 */
|
||||||
|
rotate(315deg);
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
th.rotate-column-header > div > span {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
th.rotate {
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
th.rotate > div {
|
||||||
|
/* place div at bottom left of the th parent */
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
/* Make sure short labels still meet the corner of the parent otherwise you'll get a gap */
|
||||||
|
text-align: left;
|
||||||
|
/* Move the top left corner of the span's bottom-border to line up with the top left corner of the td's border-right border so that the border corners are matched
|
||||||
|
* Rotate 315 (-45) degrees about matched border corners */
|
||||||
|
transform:
|
||||||
|
translate(calc(100% - var(--table-border-width) / 2), var(--table-border-width))
|
||||||
|
rotate(-45deg);
|
||||||
|
transform-origin: 0% calc(100% - var(--table-border-width));
|
||||||
|
transition: transform 500ms;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
th.rotate > div > span {
|
||||||
|
/* make sure the bottom of the span is matched up with the bottom of the parent div */
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
border-bottom: var(--table-border-width) solid #383e5d;
|
||||||
|
transition: border-bottom-color 500ms;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
table.rotate-table > tbody td {
|
||||||
|
border-right: var(--table-border-width) solid #383e5d;
|
||||||
|
/* make sure this is at least as wide as sqrt(2) * height of the tallest letter in your font or the headers will overlap each other*/
|
||||||
|
min-width: 30px;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-left: 5px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1650px) {
|
||||||
|
th.rotate > div {
|
||||||
|
transform: translate(15px, 0px) rotate(0deg);
|
||||||
|
}
|
||||||
|
th.rotate > div > span {
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<tr class="rounded">
|
<tr class="rounded">
|
||||||
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th>
|
<th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
|
||||||
<th></th>
|
<th class="rotate"><div><span>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</span></div></th>
|
||||||
|
{% for permission in data['permissions_all'] %}
|
||||||
|
<th class="rotate"><div><span>{{ permission.name }}</span></div></th>
|
||||||
|
{% end %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for user in data['users'] %}
|
{% for server in data['servers_all'] %}
|
||||||
{% for ruser in data['user-roles'][user.user_id] %}
|
<tr>
|
||||||
{% if ruser == data['role']['role_name'] %}
|
<td>{{ server['server_name'] }}</td>
|
||||||
<tr>
|
<td>
|
||||||
<td>{{ user.username }}</td>
|
<input type="checkbox" class="" onclick="enable_disable(event)" data-id="{{server['server_id']}}"
|
||||||
<td>
|
id="server_{{ server['server_id'] }}_access"
|
||||||
<a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-user-edit"></i></a>
|
name="server_{{ server['server_id'] }}_access"
|
||||||
</td>
|
{{ 'checked' if server['server_id'] in data['role']['servers'] else '' }}
|
||||||
</tr>
|
autocomplete="off" value="1">
|
||||||
|
</td>
|
||||||
|
{% for permission in data['permissions_all'] %}
|
||||||
|
{% if server['server_id'] in data['role']['servers'] %}
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" class="{{server['server_id']}}_perms"
|
||||||
|
id="permission_{{ server['server_id'] }}_{{ permission.name }}"
|
||||||
|
name="permission_{{ server['server_id'] }}_{{ permission.name }}"
|
||||||
|
{{ 'checked' if permission in data['permissions_dict'].get(server['server_id'], []) else '' }}
|
||||||
|
autocomplete="off" value="1">
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" class="{{server['server_id']}}_perms"
|
||||||
|
id="permission_{{ server['server_id'] }}_{{ permission.name }}"
|
||||||
|
name="permission_{{ server['server_id'] }}_{{ permission.name }}"
|
||||||
|
autocomplete="off" value="1" disabled>
|
||||||
|
</td>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col-md-3 col-sm-12">
|
<div class="card">
|
||||||
<div class="card">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<div class="card-body">
|
<h4 class="card-title"><i class="fas fa-settings"></i> {{ translate('panelConfig', 'save', data['lang']) }}</h4>
|
||||||
<h4 class="card-title">{{ translate('rolesConfig', 'roleConfigArea', data['lang']) }}</h4>
|
|
||||||
<p class="card-description"> {{ translate('rolesConfig', 'configDesc', data['lang']) }}</p>
|
|
||||||
<blockquote class="blockquote">
|
|
||||||
<p class="mb-0">
|
|
||||||
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
|
|
||||||
<br />
|
|
||||||
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
</blockquote>
|
|
||||||
<div class="text-center">
|
|
||||||
{% if data['new_role'] %}
|
|
||||||
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a><br />
|
|
||||||
<small>{{ translate('rolesConfig', 'doesNotExist', data['lang']) }}</small>
|
|
||||||
{% else %}
|
|
||||||
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a>
|
|
||||||
{% end %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
|
||||||
|
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr class="rounded">
|
||||||
|
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in data['users'] %}
|
||||||
|
{% for ruser in data['user-roles'][user.user_id] %}
|
||||||
|
{% if ruser == data['role']['role_name'] %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.username }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-user-edit"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{{ translate('rolesConfig', 'roleConfigArea', data['lang']) }}</h4>
|
||||||
|
<p class="card-description"> {{ translate('rolesConfig', 'configDesc', data['lang']) }}</p>
|
||||||
|
<blockquote class="blockquote">
|
||||||
|
<p class="mb-0">
|
||||||
|
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
|
||||||
|
<br />
|
||||||
|
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
<div class="text-center">
|
||||||
|
{% if data['new_role'] %}
|
||||||
|
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a><br />
|
||||||
|
<small>{{ translate('rolesConfig', 'doesNotExist', data['lang']) }}</small>
|
||||||
|
{% else %}
|
||||||
|
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
@ -219,7 +277,19 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
function enable_disable(event) {
|
||||||
|
let server_id = event.target.getAttribute('data-id');
|
||||||
|
console.log(server_id);
|
||||||
|
if (document.getElementById("server_" + server_id + "_access").checked) {
|
||||||
|
$('.'+server_id+'_perms').attr('disabled', false);
|
||||||
|
$('.'+server_id+'_perms').attr('enabled', true);
|
||||||
|
}else{
|
||||||
|
$('.'+server_id+'_perms').prop('checked', false);
|
||||||
|
$('.'+server_id+'_perms').attr('disabled', true);
|
||||||
|
$('.'+server_id+'_perms').attr('enabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
|
@ -9,22 +9,22 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
{% if data['new_user'] %}
|
{% if data['new_user'] %}
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
|
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
|
||||||
<br />
|
<br />
|
||||||
<small>UID: N/A</small>
|
<small>UID: N/A</small>
|
||||||
</h4>
|
</h4>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('userConfig', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
|
{{ translate('userConfig', 'pageTitle', data['lang']) }}
|
||||||
<br />
|
<br />
|
||||||
<small>UID: {{ data['user']['user_id'] }}</small>
|
<small>UID: {{ data['user']['user_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,198 +37,251 @@
|
|||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="/panel/{{ 'add_user' if data['new_user'] else 'edit_user' }}?id={{ data['user']['user_id'] }}&subpage=config" role="tab" aria-selected="true">
|
<a class="nav-link active"
|
||||||
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }} - {{ data['user']['user_id'] }}</a>
|
href="/panel/{{ 'add_user' if data['new_user'] else 'edit_user' }}?id={{ data['user']['user_id'] }}&subpage=config"
|
||||||
</li>
|
role="tab" aria-selected="true">
|
||||||
{% if not data['new_user'] %}
|
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }}</a>
|
||||||
<li class="nav-item">
|
</li>
|
||||||
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab" aria-selected="false">
|
{% if not data['new_user'] %}
|
||||||
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }} - {{ data['user']['user_id'] }}</a>
|
<li class="nav-item">
|
||||||
</li>
|
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab"
|
||||||
{% end %}
|
aria-selected="false">
|
||||||
</ul>
|
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }}</a>
|
||||||
|
</li>
|
||||||
|
{% end %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
{% if data['new_user'] %}
|
{% if data['new_user'] %}
|
||||||
<form id="user_form" class="forms-sample" method="post" action="/panel/add_user">
|
<form id="user_form" class="forms-sample" method="post" action="/panel/add_user">
|
||||||
{% else %}
|
{% else %}
|
||||||
<form id="user_form" class="forms-sample" method="post" action="/panel/edit_user">
|
<form id="user_form" class="forms-sample" method="post" action="/panel/edit_user">
|
||||||
{% end %}
|
{% end %}
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
|
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
|
||||||
<input type="hidden" name="subpage" value="config">
|
<input type="hidden" name="subpage" value="config">
|
||||||
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings', data['lang']) }} - {{ data['user']['user_id'] }}</h4>
|
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings',
|
||||||
</div>
|
data['lang']) }}</h4>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<div class="form-group">
|
<div class="card-body">
|
||||||
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="username" id="username" value="{{ data['user']['username'] }}" placeholder="User Name" >
|
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang'])
|
||||||
</div>
|
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang'])
|
||||||
<div class="form-group">
|
}}</small> </label>
|
||||||
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
|
<input type="text" class="form-control" name="username" id="username"
|
||||||
<input type="password" class="form-control" name="password0" id="password0" value="" placeholder="Password" >
|
value="{{ data['user']['username'] }}" placeholder="User Name">
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }} - {{ data['user']['user_id'] }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
|
|
||||||
<input type="password" class="form-control" name="password1" id="password1" value="" placeholder="Repeat Password" >
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
|
|
||||||
<input type="email" class="form-control" name="email" id="email" value="{{ data['user']['email'] }}" placeholder="Gravatar Email" >
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang']) }}</label>
|
|
||||||
<select class="form-select form-control form-control-lg select-css" id="language" name="language" form="user_form">
|
|
||||||
{% for lang in data['languages'] %}
|
|
||||||
{% if not 'incomplete' in lang %}
|
|
||||||
<option value="{{lang}}">{{lang}}</option>
|
|
||||||
{% else %}
|
|
||||||
<option value="{{lang}}" disabled>{{lang}}</option>
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<div class="card">
|
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang'])
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }}
|
||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('userConfig', 'userRoles', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'userRolesDesc', data['lang']) }}</small></h4>
|
</small> </label>
|
||||||
</div>
|
<input type="password" class="form-control" name="password0" id="password0" value=""
|
||||||
<div class="card-body">
|
placeholder="Password">
|
||||||
<div class="form-group">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr class="rounded">
|
|
||||||
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for role in data['roles_all'] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ role.role_name }}</td>
|
|
||||||
<td>
|
|
||||||
{% if role.role_id in data['user']['roles'] %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" checked="" value="1">
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" value="1">
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<!-- Put Permissions Crafty part here -->
|
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }}
|
||||||
|
<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang'])
|
||||||
<div class="card">
|
}}</small> </label>
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<input type="password" class="form-control" name="password1" id="password1" value=""
|
||||||
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc', data['lang']) }}</small></h4>
|
placeholder="Repeat Password">
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr class="rounded">
|
|
||||||
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for permission in data['permissions_all'] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ permission.name }}</td>
|
|
||||||
<td>
|
|
||||||
{% if permission in data['permissions_list'] %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" checked="" value="1">
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" value="1">
|
|
||||||
{% end %}
|
|
||||||
</td>
|
|
||||||
<td><input type="text" class="form-control" name="quantity_{{ permission.name }}" id="quantity_{{ permission.name }}" value="{{ data['quantity_server'][permission.name] }}"></td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<div class="form-check-flat">
|
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang'])
|
||||||
<label for="enabled" class="form-check-label ml-4 mb-4">
|
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang'])
|
||||||
{% if data['user']['enabled'] %}
|
}}</small> </label>
|
||||||
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked="" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
|
<input type="email" class="form-control" name="email" id="email"
|
||||||
|
value="{{ data['user']['email'] }}" placeholder="Gravatar Email">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang'])
|
||||||
|
}}</label>
|
||||||
|
<select class="form-select form-control form-control-lg select-css" id="language"
|
||||||
|
name="language" form="user_form">
|
||||||
|
{% for lang in data['languages'] %}
|
||||||
|
{% if not 'incomplete' in lang %}
|
||||||
|
<option value="{{lang}}">{{lang}}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
|
<option value="{{lang}}" disabled>{{lang}}</option>
|
||||||
{% end %}
|
{% end %}
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="superuser" class="form-check-label ml-4 mb-4">
|
|
||||||
{% if data['user']['superuser'] %}
|
|
||||||
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" checked="" value="1" {{ data['super-disabled'] }} >{{ translate('userConfig', 'super', data['lang']) }}
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" {{ data['super-disabled'] }} value="1" >{{ translate('userConfig', 'super', data['lang']) }}
|
|
||||||
{% end %}
|
{% end %}
|
||||||
</label>
|
</select>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
|
|
||||||
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6 col-sm-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title"><i class="fas fa-user-cog"></i> {{ translate('userConfig', 'configArea', data['lang']) }}</h4>
|
|
||||||
<p class="card-description"> {{ translate('userConfig', 'configAreaDesc', data['lang']) }}</p>
|
|
||||||
<blockquote class="blockquote">
|
|
||||||
<p class="mb-0">
|
|
||||||
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
|
|
||||||
<br />
|
|
||||||
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
|
|
||||||
<br />
|
|
||||||
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
|
|
||||||
<br />
|
|
||||||
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
</blockquote>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
</div>
|
||||||
{% if data['new_user'] %}
|
|
||||||
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
|
|
||||||
<small>{{ translate('userConfig', 'notExist', data['lang']) }}</small>
|
|
||||||
{% elif data['user']['superuser'] %}
|
|
||||||
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
|
|
||||||
<small>{{ translate('userConfig', 'delSuper', data['lang']) }}</small>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a>
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('userConfig', 'userRoles',
|
||||||
|
data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'userRolesDesc',
|
||||||
|
data['lang']) }}</small></h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="card-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr class="rounded">
|
||||||
|
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
|
||||||
|
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for role in data['roles_all'] %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ role.role_name }}</td>
|
||||||
|
<td>
|
||||||
|
{% if role.role_id in data['user']['roles'] %}
|
||||||
|
<input type="checkbox" class="form-check-input"
|
||||||
|
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
|
||||||
|
checked="" value="1">
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input"
|
||||||
|
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
|
||||||
|
value="1">
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Put Permissions Crafty part here -->
|
||||||
|
{% if data['superuser'] %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms',
|
||||||
|
data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc',
|
||||||
|
data['lang']) }}</small></h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr class="rounded">
|
||||||
|
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
|
||||||
|
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
|
||||||
|
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for permission in data['permissions_all'] %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ permission.name }}</td>
|
||||||
|
<td>
|
||||||
|
{% if permission in data['permissions_list'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}"
|
||||||
|
name="permission_{{ permission.name }}" checked="" value="1">
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}"
|
||||||
|
name="permission_{{ permission.name }}" value="1">
|
||||||
|
{% end %}
|
||||||
|
</td>
|
||||||
|
<td><input type="text" class="form-control" name="quantity_{{ permission.name }}"
|
||||||
|
id="quantity_{{ permission.name }}"
|
||||||
|
value="{{ data['quantity_server'][permission.name] }}"></td>
|
||||||
|
</tr>
|
||||||
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
<div class="form-check-flat">
|
||||||
|
<label for="enabled" class="form-check-label ml-4 mb-4">
|
||||||
|
{% if data['user']['enabled'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked=""
|
||||||
|
value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">{{
|
||||||
|
translate('userConfig', 'enabled', data['lang']) }}
|
||||||
|
{% end %}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="superuser" class="form-check-label ml-4 mb-4">
|
||||||
|
{% if data['user']['superuser'] %}
|
||||||
|
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser"
|
||||||
|
name="superuser" checked="" value="1" {{ data['super-disabled'] }}>{{ translate('userConfig',
|
||||||
|
'super', data['lang']) }}
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser"
|
||||||
|
name="superuser" {{ data['super-disabled'] }} value="1">{{ translate('userConfig', 'super',
|
||||||
|
data['lang']) }}
|
||||||
|
{% end %}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="hints" class="form-check-label ml-4 mb-4">
|
||||||
|
{% if data['user']['hints'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="hints" name="hints" checked=""
|
||||||
|
value="1">Enable Hints?
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="hints" name="hints" value="1"> Enable
|
||||||
|
Hints?
|
||||||
|
{% end %}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
|
||||||
|
translate('panelConfig', 'save', data['lang']) }}</button>
|
||||||
|
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i
|
||||||
|
class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title"><i class="fas fa-user-cog"></i> {{ translate('userConfig', 'configArea',
|
||||||
|
data['lang']) }}</h4>
|
||||||
|
<p class="card-description"> {{ translate('userConfig', 'configAreaDesc', data['lang']) }}</p>
|
||||||
|
<blockquote class="blockquote">
|
||||||
|
<p class="mb-0">
|
||||||
|
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
|
||||||
|
<br />
|
||||||
|
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
|
||||||
|
<br />
|
||||||
|
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
|
||||||
|
<br />
|
||||||
|
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
{% if data['new_user'] %}
|
||||||
|
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('userConfig',
|
||||||
|
'deleteUserB', data['lang']) }}</a><br />
|
||||||
|
<small>{{ translate('userConfig', 'notExist', data['lang']) }}</small>
|
||||||
|
{% elif data['user']['superuser'] %}
|
||||||
|
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> {{ translate('userConfig',
|
||||||
|
'deleteUserB', data['lang']) }}</a><br />
|
||||||
|
<small>{{ translate('userConfig', 'delSuper', data['lang']) }}</small>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{
|
||||||
|
translate('userConfig', 'deleteUserB', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -245,70 +298,70 @@
|
|||||||
<script>
|
<script>
|
||||||
const userId = new URLSearchParams(document.location.search).get('id')
|
const userId = new URLSearchParams(document.location.search).get('id')
|
||||||
|
|
||||||
$( ".delete-user" ).click(function() {
|
$(".delete-user").click(function () {
|
||||||
var file_to_del = $(this).data("file");
|
var file_to_del = $(this).data("file");
|
||||||
|
|
||||||
console.log("User to delete is "+userId);
|
console.log("User to delete is " + userId);
|
||||||
|
|
||||||
bootbox.confirm({
|
bootbox.confirm({
|
||||||
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %} "+userId,
|
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %} " + userId,
|
||||||
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
|
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
|
||||||
buttons: {
|
buttons: {
|
||||||
cancel: {
|
cancel: {
|
||||||
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
|
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
|
||||||
},
|
},
|
||||||
confirm: {
|
confirm: {
|
||||||
className: 'btn-outline-danger',
|
className: 'btn-outline-danger',
|
||||||
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
|
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: function (result) {
|
|
||||||
console.log(result);
|
|
||||||
if (result == true) {
|
|
||||||
location.href="/panel/remove_user?id="+userId;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
callback: function (result) {
|
||||||
|
console.log(result);
|
||||||
|
if (result === true) {
|
||||||
|
location.href = "/panel/remove_user?id=" + userId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function superConfirm() {
|
function superConfirm() {
|
||||||
if (document.getElementById('superuser').checked){
|
if (document.getElementById('superuser').checked) {
|
||||||
bootbox.confirm({
|
bootbox.confirm({
|
||||||
title: "{{ translate('panelConfig', 'superConfirmTitle', data['lang']) }}",
|
title: "{{ translate('panelConfig', 'superConfirmTitle', data['lang']) }}",
|
||||||
message: "{{ translate('panelConfig', 'superConfirm', data['lang']) }}",
|
message: "{{ translate('panelConfig', 'superConfirm', data['lang']) }}",
|
||||||
buttons: {
|
buttons: {
|
||||||
cancel: {
|
cancel: {
|
||||||
label: '<i class="fa fa-times"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}'
|
label: '<i class="fa fa-times"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}'
|
||||||
},
|
},
|
||||||
confirm: {
|
confirm: {
|
||||||
className: 'btn-outline-warning',
|
className: 'btn-outline-warning',
|
||||||
label: '<i class="fa fa-check"></i> {{ translate('serverBackups', 'confirm', data['lang']) }}'
|
label: '<i class="fa fa-check"></i> {{ translate('serverBackups', 'confirm', data['lang']) }}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: function (result) {
|
callback: function (result) {
|
||||||
if (result == true){
|
if (result === true) {
|
||||||
return;
|
return;
|
||||||
}else{
|
} else {
|
||||||
document.getElementById('superuser').checked = false;
|
document.getElementById('superuser').checked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
return r ? r[1] : undefined;
|
return r ? r[1] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log( "ready!" );
|
console.log("ready!");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,39 +5,60 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-4 mr-2">
|
<div class="col-sm-4 mr-2">
|
||||||
{% if data['server_stats']['running'] %}
|
{% if data['server_stats']['running'] %}
|
||||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-success">{{ translate('serverStats', 'online', data['lang']) }}</span><br />
|
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status"
|
||||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started">{{ data['server_stats']['started'] }}</span><br />
|
class="text-success">{{ translate('serverStats', 'online', data['lang']) }}</span><br />
|
||||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{ translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
|
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started">{{
|
||||||
|
data['server_stats']['started'] }}</span><br />
|
||||||
|
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{
|
||||||
|
translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
|
||||||
{% elif data['server_stats']['crashed'] %}
|
{% elif data['server_stats']['crashed'] %}
|
||||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span><br />
|
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-danger">
|
||||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span><br />
|
<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang'])
|
||||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span>
|
}}</span><br />
|
||||||
|
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started"
|
||||||
|
class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed',
|
||||||
|
data['lang']) }}</span><br />
|
||||||
|
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-danger">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status"
|
||||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span>
|
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started"
|
||||||
|
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||||
|
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime"
|
||||||
|
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span>
|
||||||
{% end %}
|
{% end %}
|
||||||
<br>
|
<br>
|
||||||
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{ data['serverTZ'] }}</span>
|
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{
|
||||||
|
data['serverTZ'] }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 mr-2">
|
<div class="col-sm-3 mr-2">
|
||||||
<b>{{ translate('serverStats', 'cpuUsage', data['lang']) }}:</b> <span id="cpu">{{ data['server_stats']['cpu'] }}%</span> <br />
|
<b>{{ translate('serverStats', 'cpuUsage', data['lang']) }}:</b> <span id="cpu">{{
|
||||||
<b>{{ translate('serverStats', 'memUsage', data['lang']) }}:</b> <span id="mem" >{{ data['server_stats']['mem'] }}</span> <br />
|
data['server_stats']['cpu'] }}%</span> <br />
|
||||||
|
<b>{{ translate('serverStats', 'memUsage', data['lang']) }}:</b> <span id="mem">{{
|
||||||
|
data['server_stats']['mem'] }}</span> <br />
|
||||||
{% if data['server_stats']['int_ping_results'] %}
|
{% if data['server_stats']['int_ping_results'] %}
|
||||||
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players" >{{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}</span><br />
|
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players">{{
|
||||||
|
data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}</span><br />
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players" >0/0</span><br />
|
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players">0/0</span><br />
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 mr-2">
|
<div class="col-sm-3 mr-2">
|
||||||
{% if data['server_stats']['version'] != 'False' %}
|
{% if data['server_stats']['version'] != 'False' %}
|
||||||
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{ data['server_stats']['version'] }}</span><br />
|
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{
|
||||||
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd" class="input_motd">{{ data['server_stats']['desc'] }}</span> <br />
|
data['server_stats']['version'] }}</span><br />
|
||||||
|
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd"
|
||||||
|
style="max-width: 10px; max-height: 10px" class="input_motd">{{ data['server_stats']['desc'] }}</span>
|
||||||
|
<br />
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
|
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{
|
||||||
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd" class="input_motd">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
|
translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
|
||||||
|
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span
|
||||||
|
style="max-width: 10px; max-height: 10px" id="input_motd" class="input_motd">{{ translate('serverStats',
|
||||||
|
'unableToConnect', data['lang']) }}</span> <br />
|
||||||
{% end %}
|
{% end %}
|
||||||
<b>Server Type: <span class="text-info">{{data['server_stats']['server_type']}}</span></b>
|
<b>Server Type: <span class="text-info">{{data['server_stats']['server_type']}}</span></b>
|
||||||
|
|
||||||
@ -64,6 +85,15 @@
|
|||||||
seconds: duration._data.seconds
|
seconds: duration._data.seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Math.round(duration._data.days)) {
|
||||||
|
obj = {
|
||||||
|
days: Math.round(duration._data.days),
|
||||||
|
hours: Math.round(duration._data.hours -= duration._data.days * 24),
|
||||||
|
minutes: duration._data.minutes,
|
||||||
|
seconds: duration._data.seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
output = Object.entries(obj)
|
output = Object.entries(obj)
|
||||||
.map(([type, num]) => {
|
.map(([type, num]) => {
|
||||||
// make them strings
|
// make them strings
|
||||||
@ -135,9 +165,9 @@
|
|||||||
server_input_motd = document.getElementById('input_motd');
|
server_input_motd = document.getElementById('input_motd');
|
||||||
|
|
||||||
/* TODO Update each element */
|
/* TODO Update each element */
|
||||||
if (server.running){
|
if (server.running) {
|
||||||
server_status.setAttribute("class", "text-success");
|
server_status.setAttribute("class", "text-success");
|
||||||
server_status.innerHTML = `{{ translate('serverStats', 'online', data['lang']) }}`;
|
server_status.innerHTML = `{{ translate('serverStats', 'online', data['lang']) }}`;
|
||||||
|
|
||||||
startedUTC = server.started;
|
startedUTC = server.started;
|
||||||
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
||||||
@ -156,9 +186,8 @@
|
|||||||
uptimeLoop = setInterval(calculateUptime, 1000);
|
uptimeLoop = setInterval(calculateUptime, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
if (server.crashed) {
|
||||||
if (server.crashed){
|
|
||||||
server_status.setAttribute("class", "text-danger");
|
server_status.setAttribute("class", "text-danger");
|
||||||
server_status.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
|
server_status.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
|
||||||
server_started.setAttribute("class", "text-danger");
|
server_started.setAttribute("class", "text-danger");
|
||||||
@ -167,37 +196,33 @@
|
|||||||
uptimeLoop = null;
|
uptimeLoop = null;
|
||||||
server_uptime.setAttribute("class", "text-danger");
|
server_uptime.setAttribute("class", "text-danger");
|
||||||
server_uptime.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
|
server_uptime.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
|
||||||
}else{
|
} else {
|
||||||
server_status.setAttribute("class", "text-warning");
|
server_status.setAttribute("class", "text-warning");
|
||||||
server_status.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
server_status.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||||
server_started.setAttribute("class", "text-warning");
|
server_started.setAttribute("class", "text-warning");
|
||||||
server_started.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
server_started.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||||
clearInterval(uptimeLoop);
|
clearInterval(uptimeLoop);
|
||||||
uptimeLoop = null;
|
uptimeLoop = null;
|
||||||
server_uptime.setAttribute("class", "text-warning");
|
server_uptime.setAttribute("class", "text-warning");
|
||||||
server_uptime.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
server_uptime.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server_cpu.innerHTML = server.cpu + ` %`;
|
server_cpu.innerHTML = server.cpu + ` %`;
|
||||||
server_mem.innerHTML = server.mem;
|
server_mem.innerHTML = server.mem;
|
||||||
|
|
||||||
if (server.int_ping_results)
|
if (server.int_ping_results) {
|
||||||
{
|
|
||||||
server_players.innerHTML = server.online + `/` + server.max;
|
server_players.innerHTML = server.online + `/` + server.max;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
server_players.innerHTML = `0/0`;
|
server_players.innerHTML = `0/0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.version)
|
if (server.version) {
|
||||||
{
|
|
||||||
server_version.innerHTML = server.version;
|
server_version.innerHTML = server.version;
|
||||||
server_input_motd.innerHTML = server.desc;
|
server_input_motd.innerHTML = server.desc;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
server_version.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
server_version.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
||||||
server_input_motd.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
server_input_motd.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<div class="col-sm-12 mt-4 mb-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-expanded="false">
|
||||||
|
Server Controls
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu col-md-12" aria-labelledby="dropdownMenuButton">
|
||||||
|
{% if data['permissions']['Terminal'] in data['user_permissions'] %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'term' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false"><i class="fas fa-file-signature"></i> {{ translate('serverDetails', 'terminal', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
<!--Bedrock servers don't have logs so we'll only show it if we know it's not a bedrock server.-->
|
||||||
|
{% if data['permissions']['Logs'] in data['user_permissions'] and data['server_data']['type'] != 'minecraft-bedrock'%}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'logs' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false"><i class="fas fa-file-signature"></i> {{ translate('serverDetails', 'logs', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['permissions']['Schedule'] in data['user_permissions'] %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'schedules' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=schedules" role="tab" aria-selected="false"><i class="fas fa-clock"></i> {{ translate('serverDetails', 'schedule', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['permissions']['Backup'] in data['user_permissions'] %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"><i class="fas fa-save"></i> {{ translate('serverDetails', 'backup', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['permissions']['Files'] in data['user_permissions'] %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'files' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"><i class="fas fa-folder-tree"></i> {{ translate('serverDetails', 'files', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['permissions']['Config'] in data['user_permissions'] %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'config' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true"><i class="fas fa-cogs"></i> {{ translate('serverDetails', 'config', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['permissions']['Players'] in data['user_permissions'] and data['server_data']['type'] != 'minecraft-bedrock' %}
|
||||||
|
<a class="dropdown-item {% if data['active_link'] == 'admin_controls' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"><i class="fas fa-users"></i> {{ translate('serverDetails', 'playerControls', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,71 +24,78 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Page Title Header Ends-->
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
{% include "parts/details_stats.html %}
|
{% include "parts/details_stats.html %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
|
<span class="d-none d-sm-block">
|
||||||
{% include "parts/server_controls_list.html %}
|
{% include "parts/server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
<span class="d-block d-sm-none">
|
||||||
|
{% include "parts/m_server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<style>
|
<style>
|
||||||
.playerItem {
|
.playerItem {
|
||||||
background: #1c1e2f;
|
background: #1c1e2f;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 1rem 0px 1rem 0px;
|
margin: 1rem 0px 1rem 0px;
|
||||||
}
|
}
|
||||||
.playerItem h3 {
|
|
||||||
vertical-align: middle;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
}
|
|
||||||
.playerItem button {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playerUnban {
|
.playerItem h3 {
|
||||||
margin-bottom: 1rem;
|
vertical-align: middle;
|
||||||
}
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.banned span {
|
.playerItem button {
|
||||||
font-size: 1.1rem;
|
vertical-align: middle;
|
||||||
}
|
margin: 0.25rem;
|
||||||
</style>
|
}
|
||||||
<h2>{{ translate('serverPlayerManagement', 'players', data['lang']) }}:</h2>
|
|
||||||
<ul style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
|
|
||||||
{% for player in data['get_players']() %}
|
|
||||||
<li class="playerItem">
|
|
||||||
<h3>{{ player }}</h3>
|
|
||||||
<div class="buttons">
|
|
||||||
<button onclick="send_command_to_server('ban {{ player }}')" type="button" class="btn btn-danger">Ban</button>
|
|
||||||
<button onclick="send_command_to_server('kick {{ player }}')" type="button" class="btn btn-outline-danger">Kick</button>
|
|
||||||
<button onclick="send_command_to_server('op {{ player }}')" type="button" class="btn btn-warning">OP</button>
|
|
||||||
<button onclick="send_command_to_server('deop {{ player }}')" type="button" class="btn btn-outline-warning">De-OP</button>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% end %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-12">
|
|
||||||
<h2>{{ translate('serverPlayerManagement', 'bannedPlayers', data['lang']) }}:</h2>
|
|
||||||
<ul id="bannedPlayers" style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
|
|
||||||
<li class="playerItem banned">
|
|
||||||
<h3>{{ translate('serverPlayerManagement', 'loadingBannedPlayers', data['lang']) }}</h3>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
.playerUnban {
|
||||||
</div>
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned span {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h2>{{ translate('serverPlayerManagement', 'players', data['lang']) }}:</h2>
|
||||||
|
<ul style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
|
||||||
|
{% for player in data['get_players']() %}
|
||||||
|
<li class="playerItem">
|
||||||
|
<h3>{{ player }}</h3>
|
||||||
|
<div class="buttons">
|
||||||
|
<button onclick="send_command_to_server('ban {{ player }}')" type="button" class="btn btn-danger">Ban</button>
|
||||||
|
<button onclick="send_command_to_server('kick {{ player }}')" type="button" class="btn btn-outline-danger">Kick</button>
|
||||||
|
<button onclick="send_command_to_server('op {{ player }}')" type="button" class="btn btn-warning">OP</button>
|
||||||
|
<button onclick="send_command_to_server('deop {{ player }}')" type="button" class="btn btn-outline-warning">De-OP</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% end %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<h2>{{ translate('serverPlayerManagement', 'bannedPlayers', data['lang']) }}:</h2>
|
||||||
|
<ul id="bannedPlayers" style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
|
||||||
|
<li class="playerItem banned">
|
||||||
|
<h3>{{ translate('serverPlayerManagement', 'loadingBannedPlayers', data['lang']) }}</h3>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -104,48 +111,48 @@
|
|||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
|
const serverId = new URLSearchParams(document.location.search).get('id')
|
||||||
|
|
||||||
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
|
function getCookie(name) {
|
||||||
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
|
return r ? r[1] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
function htmlDecode(input) {
|
||||||
function getCookie(name) {
|
var e = document.createElement('textarea');
|
||||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
e.innerHTML = input;
|
||||||
return r ? r[1] : undefined;
|
// handle case of empty input
|
||||||
}
|
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
|
||||||
|
}
|
||||||
|
|
||||||
function htmlDecode(input){
|
$(document).ready(function () {
|
||||||
var e = document.createElement('textarea');
|
console.log("ready!");
|
||||||
e.innerHTML = input;
|
|
||||||
// handle case of empty input
|
|
||||||
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$( document ).ready(function() {
|
var bannedPlayers = `{{ data['banned_players'] }}`;
|
||||||
console.log( "ready!" );
|
|
||||||
|
|
||||||
var bannedPlayers = `{{ data['banned_players'] }}`;
|
var bannedPlayersDecoded = htmlDecode(bannedPlayers);
|
||||||
|
|
||||||
var bannedPlayersDecoded = htmlDecode(bannedPlayers);
|
$("#bannedPlayers").html(bannedPlayersDecoded)
|
||||||
|
|
||||||
$("#bannedPlayers").html(bannedPlayersDecoded)
|
});
|
||||||
|
|
||||||
|
function send_command_to_server(command) {
|
||||||
|
console.log(command)
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
console.log('sending command: ' + command)
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/send_command?id=' + serverId,
|
||||||
|
data: { command },
|
||||||
|
success: function (data) {
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
function send_command_to_server (command) {
|
|
||||||
console.log(command)
|
|
||||||
var token = getCookie("_xsrf")
|
|
||||||
console.log('sending command: ' + command)
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: {'X-XSRFToken': token},
|
|
||||||
url: '/ajax/send_command?id=1',
|
|
||||||
data: { command },
|
|
||||||
success: function(data){
|
|
||||||
console.log("got response:");
|
|
||||||
console.log(data);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,144 +24,161 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Page Title Header Ends-->
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
{% include "parts/details_stats.html %}
|
{% include "parts/details_stats.html %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
|
|
||||||
|
<span class="d-none d-sm-block">
|
||||||
{% include "parts/server_controls_list.html %}
|
{% include "parts/server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
<span class="d-block d-sm-none">
|
||||||
|
{% include "parts/m_server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<form class="forms-sample" method="post" action="/panel/server_backup">
|
<form class="forms-sample" method="post" action="/panel/server_backup">
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
||||||
<input type="hidden" name="subpage" value="backup">
|
<input type="hidden" name="subpage" value="backup">
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<a href="/panel/backup_now?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary" onclick="backup_started()">{{ translate('serverBackups', 'backupNow', data['lang']) }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{% if data['super_user'] %}
|
|
||||||
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }}</small> </label>
|
|
||||||
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}" >
|
|
||||||
{% end %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
{% if data['backing_up'] %}
|
||||||
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang']) }}</small> </label>
|
<div class="progress" style="height: 15px;">
|
||||||
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}" >
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar" role="progressbar" style="width:{{data['backup_stats']['percent']}}%;" aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{ data['backup_stats']['percent'] }}%</div>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="compress" class="form-check-label ml-4 mb-4"></label>
|
|
||||||
{% if data['backup_config']['compress'] %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
|
||||||
checked="" value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
|
||||||
{% else %}
|
|
||||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
|
||||||
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
|
||||||
{% end %}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{ translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
|
||||||
<br>
|
|
||||||
<button class="btn btn-primary mr-2" id="root_files_button" data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{ translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
|
||||||
</div>
|
|
||||||
<input type="number" class="form-control" name="changed" id="changed" value="0" style="visibility: hidden;"></input>
|
|
||||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" aria-hidden="true">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups', 'excludedChoose', data['lang']) }}</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="tree-ctx-item" id="main-tree-div" data-path="" style="overflow: scroll; max-height:75%;">
|
|
||||||
<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled>
|
|
||||||
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
|
||||||
<i class="far fa-folder"></i>
|
|
||||||
<i class="far fa-folder-open"></i>
|
|
||||||
{{ translate('serverFiles', 'files', data['lang']) }}
|
|
||||||
</span>
|
|
||||||
</input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
|
||||||
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{ translate('serverWizard', 'save', data['lang']) }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang']) }}</button>
|
|
||||||
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p>Backing up <span id="total_files">{{data['backup_stats']['total_files']}}</span> Files</p>
|
||||||
<div class="col-md-6 col-sm-12">
|
|
||||||
<div class="text-center">
|
|
||||||
|
|
||||||
<table class="table table-responsive dataTable" id="backup_table">
|
|
||||||
<h4 class="card-title">{{ translate('serverBackups', 'currentBackups', data['lang']) }}</h4>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="10%">{{ translate('serverBackups', 'options', data['lang']) }}</th>
|
|
||||||
<th>{{ translate('serverBackups', 'path', data['lang']) }}</th>
|
|
||||||
<th width="20%">{{ translate('serverBackups', 'size', data['lang']) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for backup in data['backup_list'] %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
|
|
||||||
<i class="fas fa-download" aria-hidden="true"></i>
|
|
||||||
{{ translate('serverBackups', 'download', data['lang']) }}
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}" class="btn btn-danger del_button">
|
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
|
||||||
{{ translate('serverBackups', 'delete', data['lang']) }}
|
|
||||||
</button>
|
|
||||||
<button data-file="{{ backup['path'] }}" class="btn btn-warning restore_button">
|
|
||||||
<i class="fas fa-undo-alt" aria-hidden="true"></i>
|
|
||||||
{{ translate('serverBackups', 'restore', data['lang']) }}
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td>{{ backup['path'] }}</td>
|
|
||||||
<td>{{ backup['size'] }}</td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12 col-sm-12">
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
|
||||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups', data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<ul>
|
|
||||||
{% for item in data['exclusions'] %}
|
|
||||||
<li>{{item}}</li>
|
|
||||||
<br>
|
|
||||||
{% end %}
|
{% end %}
|
||||||
</ul>
|
|
||||||
|
<br>
|
||||||
|
{% if not data['backing_up'] %}
|
||||||
|
<div id="backup_button" class="form-group">
|
||||||
|
<button class="btn btn-primary" id="backup_now_button">{{ translate('serverBackups', 'backupNow', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
<div class="form-group">
|
||||||
|
{% if data['super_user'] %}
|
||||||
|
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }}</small> </label>
|
||||||
|
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}">
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang']) }}</small> </label>
|
||||||
|
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="compress" class="form-check-label ml-4 mb-4"></label>
|
||||||
|
{% if data['backup_config']['compress'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
||||||
|
checked="" value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
||||||
|
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{ translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
||||||
|
<br>
|
||||||
|
<button class="btn btn-primary mr-2" id="root_files_button" data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{ translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
<input type="number" class="form-control" name="changed" id="changed" value="0" style="visibility: hidden;"></input>
|
||||||
|
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups', 'excludedChoose', data['lang']) }}</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="tree-ctx-item" id="main-tree-div" data-path="" style="overflow: scroll; max-height:75%;">
|
||||||
|
<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled>
|
||||||
|
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
||||||
|
<i class="far fa-folder"></i>
|
||||||
|
<i class="far fa-folder-open"></i>
|
||||||
|
{{ translate('serverFiles', 'files', data['lang']) }}
|
||||||
|
</span>
|
||||||
|
</input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||||
|
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{ translate('serverWizard', 'save', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang']) }}</button>
|
||||||
|
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<div class="text-center">
|
||||||
|
|
||||||
|
<table class="table table-responsive dataTable" id="backup_table">
|
||||||
|
<h4 class="card-title">{{ translate('serverBackups', 'currentBackups', data['lang']) }}</h4>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="10%">{{ translate('serverBackups', 'options', data['lang']) }}</th>
|
||||||
|
<th>{{ translate('serverBackups', 'path', data['lang']) }}</th>
|
||||||
|
<th width="20%">{{ translate('serverBackups', 'size', data['lang']) }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for backup in data['backup_list'] %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
|
||||||
|
<i class="fas fa-download" aria-hidden="true"></i>
|
||||||
|
{{ translate('serverBackups', 'download', data['lang']) }}
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}" class="btn btn-danger del_button">
|
||||||
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
|
{{ translate('serverBackups', 'delete', data['lang']) }}
|
||||||
|
</button>
|
||||||
|
<button data-file="{{ backup['path'] }}" class="btn btn-warning restore_button">
|
||||||
|
<i class="fas fa-undo-alt" aria-hidden="true"></i>
|
||||||
|
{{ translate('serverBackups', 'restore', data['lang']) }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>{{ backup['path'] }}</td>
|
||||||
|
<td>{{ backup['size'] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 col-sm-12">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups', data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
{% for item in data['exclusions'] %}
|
||||||
|
<li>{{item}}</li>
|
||||||
|
<br>
|
||||||
|
{% end %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,41 +189,44 @@
|
|||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
/* Remove default bullets */
|
/* Remove default bullets */
|
||||||
.tree-view,
|
.tree-view,
|
||||||
.tree-nested {
|
.tree-nested {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style the items */
|
/* Style the items */
|
||||||
.tree-item,
|
.tree-item,
|
||||||
.files-tree-title {
|
.files-tree-title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none; /* Prevent text selection */
|
user-select: none;
|
||||||
}
|
/* Prevent text selection */
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the caret/arrow with a unicode, and style it */
|
/* Create the caret/arrow with a unicode, and style it */
|
||||||
.tree-caret .fa-folder {
|
.tree-caret .fa-folder {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.tree-caret .fa-folder-open {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
.tree-caret .fa-folder-open {
|
||||||
.tree-caret-down .fa-folder {
|
display: none;
|
||||||
display: none;
|
}
|
||||||
}
|
|
||||||
.tree-caret-down .fa-folder-open {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide the nested list */
|
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||||
.tree-nested {
|
.tree-caret-down .fa-folder {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree-caret-down .fa-folder-open {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the nested list */
|
||||||
|
.tree-nested {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
@ -215,7 +235,7 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const server_id = new URLSearchParams(document.location.search).get('id')
|
const server_id = new URLSearchParams(document.location.search).get('id')
|
||||||
|
|
||||||
|
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
@ -224,60 +244,87 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
return r ? r[1] : undefined;
|
return r ? r[1] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function backup_started(time='5-10') {
|
function backup_started() {
|
||||||
bootbox.alert({
|
var token = getCookie("_xsrf")
|
||||||
|
document.getElementById('backup_button').style.visibility = 'hidden';
|
||||||
|
var dialog = bootbox.dialog({
|
||||||
message: "{{ translate('serverBackups', 'backupTask', data['lang']) }}",
|
message: "{{ translate('serverBackups', 'backupTask', data['lang']) }}",
|
||||||
backdrop: true
|
closeButton: false
|
||||||
|
});
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/backup_now?id=' + server_id,
|
||||||
|
success: function (data) {
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function del_backup(filename, id){
|
function del_backup(filename, id) {
|
||||||
var token = getCookie("_xsrf")
|
var token = getCookie("_xsrf")
|
||||||
|
|
||||||
data_to_send = { file_name :filename}
|
data_to_send = { file_name: filename }
|
||||||
|
|
||||||
console.log('Sending Command to delete backup: ' + filename)
|
console.log('Sending Command to delete backup: ' + filename)
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
headers: {'X-XSRFToken': token},
|
headers: { 'X-XSRFToken': token },
|
||||||
url: '/ajax/del_backup?server_id='+id,
|
url: '/ajax/del_backup?server_id=' + id,
|
||||||
data: {
|
data: {
|
||||||
file_path: filename,
|
file_path: filename,
|
||||||
id: id
|
id: id
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function (data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function restore_backup(filename, id){
|
function restore_backup(filename, id) {
|
||||||
var token = getCookie("_xsrf")
|
var token = getCookie("_xsrf")
|
||||||
var dialog = bootbox.dialog({
|
var dialog = bootbox.dialog({
|
||||||
message: '<i class="fa fa-spin fa-spinner"></i> {{ translate('serverBackups', 'restoring', data['lang']) }}',
|
message: '<i class="fa fa-spin fa-spinner"></i> {{ translate('serverBackups', 'restoring', data['lang']) }}',
|
||||||
closeButton: false
|
closeButton: false
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Sending Command to restore backup: ' + filename)
|
console.log('Sending Command to restore backup: ' + filename)
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
headers: {'X-XSRFToken': token},
|
headers: { 'X-XSRFToken': token },
|
||||||
url: '/ajax/restore_backup?server_id='+id,
|
url: '/ajax/restore_backup?server_id=' + id,
|
||||||
data: {
|
data: {
|
||||||
zip_file: filename,
|
zip_file: filename,
|
||||||
id: id
|
id: id
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function (data) {
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
location.href=('/panel/dashboard');
|
location.href = ('/panel/dashboard');
|
||||||
}, 15000);
|
}, 15000);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log( "ready!" );
|
try {
|
||||||
|
if ($('#backup_path').val() == '') {
|
||||||
|
console.log('true')
|
||||||
|
try {
|
||||||
|
document.getElementById('backup_now_button').disabled = true;
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('backup_now_button').disabled = false;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
document.getElementById('backup_now_button').disabled = false;
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("ready!");
|
||||||
$("#backup_config_box").hide();
|
$("#backup_config_box").hide();
|
||||||
$("#backup_save_note").hide();
|
$("#backup_save_note").hide();
|
||||||
|
|
||||||
@ -289,8 +336,8 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#backup_table').DataTable({
|
$('#backup_table').DataTable({
|
||||||
"order": [[ 1, "desc" ]],
|
"order": [[1, "desc"]],
|
||||||
"paging":true,
|
"paging": true,
|
||||||
"lengthChange": false,
|
"lengthChange": false,
|
||||||
"searching": true,
|
"searching": true,
|
||||||
"ordering": true,
|
"ordering": true,
|
||||||
@ -299,7 +346,7 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
"responsive": true,
|
"responsive": true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$( ".del_button" ).click(function() {
|
$(".del_button").click(function () {
|
||||||
var file_to_del = $(this).data("file");
|
var file_to_del = $(this).data("file");
|
||||||
var backup_path = $(this).data('backup_path');
|
var backup_path = $(this).data('backup_path');
|
||||||
|
|
||||||
@ -326,12 +373,12 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$( ".restore_button" ).click(function() {
|
$(".restore_button").click(function () {
|
||||||
var file_to_restore = $(this).data("file");
|
var file_to_restore = $(this).data("file");
|
||||||
|
|
||||||
|
|
||||||
bootbox.confirm({
|
bootbox.confirm({
|
||||||
title: "{{ translate('serverBackups', 'restore', data['lang']) }} "+file_to_restore,
|
title: "{{ translate('serverBackups', 'restore', data['lang']) }} " + file_to_restore,
|
||||||
message: "{{ translate('serverBackups', 'confirmRestore', data['lang']) }}",
|
message: "{{ translate('serverBackups', 'confirmRestore', data['lang']) }}",
|
||||||
buttons: {
|
buttons: {
|
||||||
cancel: {
|
cancel: {
|
||||||
@ -339,7 +386,7 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
},
|
},
|
||||||
confirm: {
|
confirm: {
|
||||||
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "restore", data['lang']) }}',
|
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "restore", data['lang']) }}',
|
||||||
className: 'btn-outline-danger'
|
className: 'btn-outline-danger'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: function (result) {
|
callback: function (result) {
|
||||||
@ -350,43 +397,46 @@ const server_id = new URLSearchParams(document.location.search).get('id')
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$("#backup_now_button").click(function () {
|
||||||
|
backup_started();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("modal-cancel").addEventListener("click", function(){
|
document.getElementById("modal-cancel").addEventListener("click", function () {
|
||||||
document.getElementById("root_files_button").classList.remove('clicked');
|
document.getElementById("root_files_button").classList.remove('clicked');
|
||||||
document.getElementById("main-tree-div").innerHTML = '<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled><span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
|
document.getElementById("main-tree-div").innerHTML = '<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled><span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
|
||||||
})
|
})
|
||||||
|
|
||||||
document.getElementById("root_files_button").addEventListener("click", function(){
|
document.getElementById("root_files_button").addEventListener("click", function () {
|
||||||
if($("#root_files_button").data('server_path') != ""){
|
if ($("#root_files_button").data('server_path') != "") {
|
||||||
if(document.getElementById('root_files_button').classList.contains('clicked')){
|
if (document.getElementById('root_files_button').classList.contains('clicked')) {
|
||||||
show_file_tree();
|
show_file_tree();
|
||||||
return;
|
return;
|
||||||
}else{
|
} else {
|
||||||
document.getElementById('root_files_button').classList.add('clicked');
|
document.getElementById('root_files_button').classList.add('clicked');
|
||||||
document.getElementById("changed").value = 1;
|
document.getElementById("changed").value = 1;
|
||||||
}
|
}
|
||||||
path = $("#root_files_button").data('server_path')
|
path = $("#root_files_button").data('server_path')
|
||||||
console.log($("#root_files_button").data('server_path'))
|
console.log($("#root_files_button").data('server_path'))
|
||||||
var token = getCookie("_xsrf");
|
var token = getCookie("_xsrf");
|
||||||
var dialog = bootbox.dialog({
|
var dialog = bootbox.dialog({
|
||||||
message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Please wait while we gather your files...</p>',
|
message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Please wait while we gather your files...</p>',
|
||||||
closeButton: false
|
closeButton: false
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
headers: {'X-XSRFToken': token},
|
headers: { 'X-XSRFToken': token },
|
||||||
url: '/ajax/backup_select?id='+server_id+'&path='+path,
|
url: '/ajax/backup_select?id=' + server_id + '&path=' + path,
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
bootbox.alert("You must input a path before selecting this button");
|
bootbox.alert("You must input a path before selecting this button");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (webSocket) {
|
if (webSocket) {
|
||||||
webSocket.on('send_temp_path', function (data) {
|
webSocket.on('send_temp_path', function (data) {
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
var x = document.querySelector('.bootbox');
|
var x = document.querySelector('.bootbox');
|
||||||
if (x) {
|
if (x) {
|
||||||
x.remove()
|
x.remove()
|
||||||
@ -399,7 +449,27 @@ document.getElementById("modal-cancel").addEventListener("click", function(){
|
|||||||
getTreeView(data.path);
|
getTreeView(data.path);
|
||||||
show_file_tree();
|
show_file_tree();
|
||||||
|
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (webSocket) {
|
||||||
|
webSocket.on('backup_status', function (backup) {
|
||||||
|
if (backup.percent >= 100) {
|
||||||
|
document.getElementById('backup_progress_bar').innerHTML = '100%';
|
||||||
|
document.getElementById('backup_progress_bar').style.width = '100%';
|
||||||
|
setTimeout(function () {
|
||||||
|
window.location.reload(1);
|
||||||
|
}, 5000);
|
||||||
|
} else {
|
||||||
|
document.getElementById('backup_progress_bar').innerHTML = backup.percent + '%';
|
||||||
|
document.getElementById('backup_progress_bar').style.width = backup.percent + '%';
|
||||||
|
document.getElementById('total_files').innerHTML = backup.total_files;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (webSocket) {
|
||||||
|
webSocket.on('backup_reload', function (backup) {
|
||||||
|
location.reload()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,9 +478,9 @@ document.getElementById("modal-cancel").addEventListener("click", function(){
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/ajax/get_backup_tree?id='+server_id+'&path='+path,
|
url: '/ajax/get_backup_tree?id=' + server_id + '&path=' + path,
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
success: function(data){
|
success: function (data) {
|
||||||
console.log("got response:");
|
console.log("got response:");
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
@ -418,10 +488,10 @@ document.getElementById("modal-cancel").addEventListener("click", function(){
|
|||||||
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
||||||
text = dataArr.join('\n');
|
text = dataArr.join('\n');
|
||||||
|
|
||||||
try{
|
try {
|
||||||
document.getElementById('main-tree-div').innerHTML += text;
|
document.getElementById('main-tree-div').innerHTML += text;
|
||||||
document.getElementById('main-tree').parentElement.classList.add("clicked");
|
document.getElementById('main-tree').parentElement.classList.add("clicked");
|
||||||
}catch{
|
} catch {
|
||||||
document.getElementById('files-tree').innerHTML = text;
|
document.getElementById('files-tree').innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,57 +504,57 @@ document.getElementById("modal-cancel").addEventListener("click", function(){
|
|||||||
}
|
}
|
||||||
function getToggleMain(event) {
|
function getToggleMain(event) {
|
||||||
path = event.target.parentElement.getAttribute('data-path');
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
document.getElementById("files-tree").classList.toggle("d-block");
|
document.getElementById("files-tree").classList.toggle("d-block");
|
||||||
document.getElementById(path+"span").classList.toggle("tree-caret-down");
|
document.getElementById(path + "span").classList.toggle("tree-caret-down");
|
||||||
document.getElementById(path+"span").classList.toggle("tree-caret");
|
document.getElementById(path + "span").classList.toggle("tree-caret");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getDirView(event) {
|
function getDirView(event) {
|
||||||
path = event.target.parentElement.getAttribute('data-path');
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
|
||||||
if (document.getElementById(path).classList.contains('clicked')){
|
if (document.getElementById(path).classList.contains('clicked')) {
|
||||||
|
|
||||||
var toggler = document.getElementById(path+"span");
|
var toggler = document.getElementById(path + "span");
|
||||||
|
|
||||||
if (toggler.classList.contains('files-tree-title')){
|
if (toggler.classList.contains('files-tree-title')) {
|
||||||
document.getElementById(path+"ul").classList.toggle("d-block");
|
document.getElementById(path + "ul").classList.toggle("d-block");
|
||||||
document.getElementById(path+"span").classList.toggle("tree-caret-down");
|
document.getElementById(path + "span").classList.toggle("tree-caret-down");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}else{
|
} else {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/ajax/get_backup_dir?id='+server_id+'&path='+path,
|
url: '/ajax/get_backup_dir?id=' + server_id + '&path=' + path,
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
success: function(data){
|
success: function (data) {
|
||||||
console.log("got response:");
|
console.log("got response:");
|
||||||
|
|
||||||
dataArr = data.split('\n');
|
dataArr = data.split('\n');
|
||||||
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
||||||
text = dataArr.join('\n');
|
text = dataArr.join('\n');
|
||||||
|
|
||||||
try{
|
try {
|
||||||
document.getElementById(path+"span").classList.add('tree-caret-down');
|
document.getElementById(path + "span").classList.add('tree-caret-down');
|
||||||
document.getElementById(path).innerHTML += text;
|
document.getElementById(path).innerHTML += text;
|
||||||
document.getElementById(path).classList.add("clicked");
|
document.getElementById(path).classList.add("clicked");
|
||||||
}catch{
|
} catch {
|
||||||
console.log("Bad")
|
console.log("Bad")
|
||||||
}
|
}
|
||||||
|
|
||||||
var toggler = document.getElementById(path);
|
var toggler = document.getElementById(path);
|
||||||
|
|
||||||
if (toggler.classList.contains('files-tree-title')){
|
if (toggler.classList.contains('files-tree-title')) {
|
||||||
document.getElementById(path+"span").addEventListener("click", function caretListener() {
|
document.getElementById(path + "span").addEventListener("click", function caretListener() {
|
||||||
document.getElementById(path+"ul").classList.toggle("d-block");
|
document.getElementById(path + "ul").classList.toggle("d-block");
|
||||||
document.getElementById(path+"span").classList.toggle("tree-caret-down");
|
document.getElementById(path + "span").classList.toggle("tree-caret-down");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
function show_file_tree() {
|
||||||
function show_file_tree(){
|
|
||||||
$("#dir_select").modal();
|
$("#dir_select").modal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,208 +24,230 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Page Title Header Ends-->
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
{% include "parts/details_stats.html %}
|
{% include "parts/details_stats.html %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
{% include "parts/server_controls_list.html %}
|
|
||||||
|
|
||||||
<div class="row">
|
<span class="d-none d-sm-block">
|
||||||
<div class="col-md-12 col-sm-12" style="overflow-x:auto;">
|
{% include "parts/server_controls_list.html %}
|
||||||
<div class="card">
|
</span>
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<span class="d-block d-sm-none">
|
||||||
<h4 class="card-title"><i class="fas fa-calendar"></i> Scheduled Tasks</h4>
|
{% include "parts/m_server_controls_list.html %}
|
||||||
<span class="too_small" title="{{ translate('serverSchedules', 'cannotSee', data['lang']) }}", data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}", data-placement="bottom"></span>
|
</span>
|
||||||
<div><button onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`" class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div>
|
|
||||||
</div>
|
<div class="row">
|
||||||
<div class="card-body">
|
<div class="col-md-12 col-sm-12" style="overflow-x:auto;">
|
||||||
<table class="table table-hover d-none d-lg-block responsive-table" id="schedule_table" width="100%" style="table-layout:fixed;">
|
<div class="card">
|
||||||
<thead>
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<tr class="rounded">
|
<h4 class="card-title"><i class="fas fa-calendar"></i> Scheduled Tasks</h4>
|
||||||
<th style="width: 2%; min-width: 10px;">ID</th>
|
{% if data['user_data']['hints'] %}
|
||||||
<th style="width: 23%; min-width: 50px;">Action</th>
|
<span class="too_small" title="{{ translate('serverSchedules', 'cannotSee', data['lang']) }}" ,
|
||||||
<th style="width: 40%; min-width: 50px;">Command</th>
|
data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}" ,
|
||||||
<th style="width: 10%; min-width: 50px;">Interval</th>
|
data-placement="bottom"></span>
|
||||||
<th style="width: 10%; min-width: 50px;">Start Time</th>
|
{% end %}
|
||||||
<th style="width: 10%; min-width: 50px;">Enabled</th>
|
<div><button
|
||||||
<th style="width: 10%; min-width: 50px;">Edit</th>
|
onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`"
|
||||||
</tr>
|
class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div>
|
||||||
</thead>
|
</div>
|
||||||
<tbody>
|
<div class="card-body">
|
||||||
{% for schedule in data['schedules'] %}
|
<table class="table table-hover d-none d-lg-block responsive-table" id="schedule_table" width="100%"
|
||||||
<tr>
|
style="table-layout:fixed;">
|
||||||
<td id="{{schedule.schedule_id}}" class="id">
|
<thead>
|
||||||
<p>{{schedule.schedule_id}}</p>
|
<tr class="rounded">
|
||||||
</td>
|
<th style="width: 2%; min-width: 10px;">ID</th>
|
||||||
<td id="{{schedule.action}}" class="action">
|
<th style="width: 23%; min-width: 50px;">Action</th>
|
||||||
<p>{{schedule.action}}</p>
|
<th style="width: 40%; min-width: 50px;">Command</th>
|
||||||
</td>
|
<th style="width: 10%; min-width: 50px;">Interval</th>
|
||||||
<td id="{{schedule.command}}" class="action" style="overflow: scroll; max-width: 30px;">
|
<th style="width: 10%; min-width: 50px;">Start Time</th>
|
||||||
<p>{{schedule.command}}</p>
|
<th style="width: 10%; min-width: 50px;">Enabled</th>
|
||||||
</td>
|
<th style="width: 10%; min-width: 50px;">Edit</th>
|
||||||
<td id="{{schedule.interval}}" class="action">
|
</tr>
|
||||||
{% if schedule.interval != '' %}
|
</thead>
|
||||||
<p>Every</p>
|
<tbody>
|
||||||
<p>{{schedule.interval}} {{schedule.interval_type}}</p>
|
{% for schedule in data['schedules'] %}
|
||||||
{% elif schedule.interval_type == 'reaction' %}
|
<tr>
|
||||||
<p>{{schedule.interval_type}}<br><br>child of ID: {{ schedule.parent }}</p>
|
<td id="{{schedule.schedule_id}}" class="id">
|
||||||
{% else %}
|
<p>{{schedule.schedule_id}}</p>
|
||||||
<p>Cron String:</p>
|
</td>
|
||||||
<p>{{schedule.cron_string}}</p>
|
<td id="{{schedule.action}}" class="action">
|
||||||
{% end %}
|
<p>{{schedule.action}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td id="{{schedule.start_time}}" class="action">
|
<td id="{{schedule.command}}" class="action" style="overflow: scroll; max-width: 30px;">
|
||||||
<p>{{schedule.start_time}}</p>
|
<p>{{schedule.command}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td id="{{schedule.enabled}}" class="action">
|
<td id="{{schedule.interval}}" class="action">
|
||||||
|
{% if schedule.interval != '' %}
|
||||||
|
<p>Every</p>
|
||||||
|
<p>{{schedule.interval}} {{schedule.interval_type}}</p>
|
||||||
|
{% elif schedule.interval_type == 'reaction' %}
|
||||||
|
<p>{{schedule.interval_type}}<br><br>child of ID: {{ schedule.parent }}</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Cron String:</p>
|
||||||
|
<p>{{schedule.cron_string}}</p>
|
||||||
|
{% end %}
|
||||||
|
</td>
|
||||||
|
<td id="{{schedule.start_time}}" class="action">
|
||||||
|
<p>{{schedule.start_time}}</p>
|
||||||
|
</td>
|
||||||
|
<td id="{{schedule.enabled}}" class="action">
|
||||||
{% if schedule.enabled %}
|
{% if schedule.enabled %}
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
<i class="fas fa-check-square"></i> Yes
|
<i class="fas fa-check-square"></i> Yes
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-danger">
|
<span class="text-danger">
|
||||||
<i class="far fa-times-square"></i> No
|
<i class="far fa-times-square"></i> No
|
||||||
</span>
|
</span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</td>
|
</td>
|
||||||
<td id="{{schedule.action}}" class="action">
|
<td id="{{schedule.action}}" class="action">
|
||||||
<button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info">
|
<button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info">
|
||||||
<i class="fas fa-pencil-alt"></i>
|
<i class="fas fa-pencil-alt"></i>
|
||||||
</button>
|
</button>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<button data-sch={{ schedule.schedule_id }} class="btn btn-danger del_button">
|
<button data-sch={{ schedule.schedule_id }} class="btn btn-danger del_button">
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<table class="table table-hover d-block d-lg-none" id="mini_schedule_table" width="100%" style="table-layout:fixed;">
|
||||||
|
<thead>
|
||||||
|
<tr class="rounded">
|
||||||
|
<th style="width: 25%; min-width: 50px;">Action</th>
|
||||||
|
<th style="max-width: 40%; min-width: 50px;">Command</th>
|
||||||
|
<th style="width: 10%; min-width: 50px;">Enabled</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for schedule in data['schedules'] %}
|
||||||
|
<tr data-toggle="modal" data-target="#task_details_{{schedule.schedule_id}}">
|
||||||
|
<td id="{{schedule.action}}" class="action">
|
||||||
|
<p>{{schedule.action}}</p>
|
||||||
|
</td>
|
||||||
|
<td id="{{schedule.command}}" class="action" style="overflow: scroll; max-width: 30px;">
|
||||||
|
<p>{{schedule.command}}</p>
|
||||||
|
</td>
|
||||||
|
<td id="{{schedule.enabled}}" class="action">
|
||||||
|
{% if schedule.enabled %}
|
||||||
|
<span class="text-success">
|
||||||
|
<i class="fas fa-check-square"></i> Yes
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-danger">
|
||||||
|
<i class="far fa-times-square"></i> No
|
||||||
|
</span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</tbody>
|
</td>
|
||||||
</table>
|
</tr>
|
||||||
<hr />
|
<!-- Modal -->
|
||||||
<table class="table table-hover d-block d-lg-none" id="mini_schedule_table" width="100%" style="table-layout:fixed;">
|
<div class="modal fade" id="task_details_{{schedule.schedule_id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
<thead>
|
<div class="modal-dialog" role="document">
|
||||||
<tr class="rounded">
|
<div class="modal-content">
|
||||||
<th style="width: 25%; min-width: 50px;">Action</th>
|
<div class="modal-header">
|
||||||
<th style="max-width: 40%; min-width: 50px;">Command</th>
|
<h5 class="modal-title" id="exampleModalLabel">Task Details</h5>
|
||||||
<th style="width: 10%; min-width: 50px;">Enabled</th>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
</tr>
|
<span aria-hidden="true">×</span>
|
||||||
</thead>
|
</button>
|
||||||
<tbody>
|
</div>
|
||||||
{% for schedule in data['schedules'] %}
|
<div class="modal-body">
|
||||||
<tr data-toggle="modal" data-target="#task_details_{{schedule.schedule_id}}">
|
<ul style="list-style: none;">
|
||||||
<td id="{{schedule.action}}" class="action">
|
<li id="{{schedule.schedule_id}}" class="id" style="border-top: .1em solid gray;">
|
||||||
<p>{{schedule.action}}</p>
|
<h4>ID</h4>
|
||||||
</td>
|
<p>{{schedule.schedule_id}}</p>
|
||||||
<td id="{{schedule.command}}" class="action" style="overflow: scroll; max-width: 30px;">
|
</li>
|
||||||
<p>{{schedule.command}}</p>
|
<li id="{{schedule.action}}" class="action" style="border-top: .1em solid gray;">
|
||||||
</td>
|
<h4>Action</h4>
|
||||||
<td id="{{schedule.enabled}}" class="action">
|
<p>{{schedule.action}}</p>
|
||||||
{% if schedule.enabled %}
|
</li>
|
||||||
<span class="text-success">
|
<li id="{{schedule.command}}" class="action" style="border-top: .1em solid gray;">
|
||||||
<i class="fas fa-check-square"></i> Yes
|
<h4>Command</h4>
|
||||||
</span>
|
<p>{{schedule.command}}</p>
|
||||||
{% else %}
|
</li>
|
||||||
<span class="text-danger">
|
<li id="{{schedule.interval}}" class="action" style="border-top: .1em solid gray;">
|
||||||
<i class="far fa-times-square"></i> No
|
{% if schedule.interval != '' %}
|
||||||
</span>
|
<h4>Interval</h4>
|
||||||
{% end %}
|
<p>Every {{schedule.interval}} {{schedule.interval_type}}</p>
|
||||||
</td>
|
{% elif schedule.interval_type == 'reaction' %}
|
||||||
</tr>
|
<h4>Interval</h4>
|
||||||
<!-- Modal -->
|
<p>{{schedule.interval_type}}<br><br>child of ID: {{ schedule.parent }}</p>
|
||||||
<div class="modal fade" id="task_details_{{schedule.schedule_id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
{% else %}
|
||||||
<div class="modal-dialog" role="document">
|
<h4>Interval</h4>
|
||||||
<div class="modal-content">
|
<p>Cron String: {{schedule.cron_string}}</p>
|
||||||
<div class="modal-header">
|
{% end %}
|
||||||
<h5 class="modal-title" id="exampleModalLabel">Task Details</h5>
|
</li>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<li id="{{schedule.start_time}}" class="action" style="border-top: .1em solid gray;">
|
||||||
<span aria-hidden="true">×</span>
|
<h4>Start Time</h4>
|
||||||
</button>
|
<p>{{schedule.start_time}}</p>
|
||||||
</div>
|
</li>
|
||||||
<div class="modal-body">
|
<li id="{{schedule.enabled}}" class="action" style="border-top: .1em solid gray; border-bottom: .1em solid gray">
|
||||||
<ul style="list-style: none;">
|
{% if schedule.enabled %}
|
||||||
<li id="{{schedule.schedule_id}}" class="id" style="border-top: .1em solid gray;">
|
<h4>Enabled</h4> <span class="text-success">
|
||||||
<h4>ID</h4><p>{{schedule.schedule_id}}</p>
|
<i class="fas fa-check-square"></i> Yes
|
||||||
</li>
|
</span>
|
||||||
<li id="{{schedule.action}}" class="action" style="border-top: .1em solid gray;">
|
{% else %}
|
||||||
<h4>Action</h4><p>{{schedule.action}}</p>
|
<h4>Enabled</h4> <span class="text-danger">
|
||||||
</li>
|
<i class="far fa-times-square"></i> No
|
||||||
<li id="{{schedule.command}}" class="action" style="border-top: .1em solid gray;">
|
</span>
|
||||||
<h4>Command</h4><p>{{schedule.command}}</p>
|
{% end %}
|
||||||
</li>
|
</li>
|
||||||
<li id="{{schedule.interval}}" class="action" style="border-top: .1em solid gray;">
|
</ul>
|
||||||
{% if schedule.interval != '' %}
|
</div>
|
||||||
<h4>Interval</h4> <p>Every {{schedule.interval}} {{schedule.interval_type}}</p>
|
<div class="modal-footer">
|
||||||
{% elif schedule.interval_type == 'reaction' %}
|
<button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info">
|
||||||
<h4>Interval</h4> <p>{{schedule.interval_type}}<br><br>child of ID: {{ schedule.parent }}</p>
|
<i class="fas fa-pencil-alt"></i> Edit
|
||||||
{% else %}
|
</button>
|
||||||
<h4>Interval</h4> <p>Cron String: {{schedule.cron_string}}</p>
|
<button data-sch={{ schedule.schedule_id }} class="btn btn-danger del_button">
|
||||||
{% end %}
|
<i class="fas fa-trash" aria-hidden="true"></i> Delete
|
||||||
</li>
|
</button>
|
||||||
<li id="{{schedule.start_time}}" class="action" style="border-top: .1em solid gray;">
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
<h4>Start Time</h4> <p>{{schedule.start_time}}</p>
|
|
||||||
</li>
|
|
||||||
<li id="{{schedule.enabled}}" class="action" style="border-top: .1em solid gray; border-bottom: .1em solid gray">
|
|
||||||
{% if schedule.enabled %}
|
|
||||||
<h4>Enabled</h4> <span class="text-success">
|
|
||||||
<i class="fas fa-check-square"></i> Yes
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<h4>Enabled</h4> <span class="text-danger">
|
|
||||||
<i class="far fa-times-square"></i> No
|
|
||||||
</span>
|
|
||||||
{% end %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info">
|
|
||||||
<i class="fas fa-pencil-alt"></i> Edit
|
|
||||||
</button>
|
|
||||||
<button data-sch={{ schedule.schedule_id }} class="btn btn-danger del_button">
|
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i> Delete
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% end %}
|
</div>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% end %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
.popover-body{
|
.popover-body {
|
||||||
color: white !important;;
|
color: white !important;
|
||||||
}
|
;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
td::-webkit-scrollbar {
|
td::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide scrollbar for IE, Edge and Firefox */
|
/* Hide scrollbar for IE, Edge and Firefox */
|
||||||
td {
|
td {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none; /* Firefox */
|
/* IE and Edge */
|
||||||
}
|
scrollbar-width: none;
|
||||||
|
/* Firefox */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
@ -236,138 +258,138 @@ td {
|
|||||||
|
|
||||||
const serverId = new URLSearchParams(document.location.search).get('id')
|
const serverId = new URLSearchParams(document.location.search).get('id')
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log('ready for JS!')
|
console.log('ready for JS!')
|
||||||
$('#schedule_table').DataTable({
|
$('#schedule_table').DataTable({
|
||||||
'order': [4, 'desc']
|
'order': [4, 'desc']
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
$( document ).ready(function() {
|
$(document).ready(function () {
|
||||||
console.log('ready for JS!')
|
console.log('ready for JS!')
|
||||||
$('#mini_schedule_table').DataTable({
|
$('#mini_schedule_table').DataTable({
|
||||||
'order': [2, 'desc']
|
'order': [2, 'desc']
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
document.getElementById('mini_schedule_table_wrapper').hidden = true;
|
document.getElementById('mini_schedule_table_wrapper').hidden = true;
|
||||||
});
|
});
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
$('[data-toggle="popover"]').popover();
|
$('[data-toggle="popover"]').popover();
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
document.getElementById('schedule_table_wrapper').hidden = true;
|
document.getElementById('schedule_table_wrapper').hidden = true;
|
||||||
document.getElementById('mini_schedule_table_wrapper').hidden = false;
|
document.getElementById('mini_schedule_table_wrapper').hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
$(window).ready(function(){
|
$(window).ready(function () {
|
||||||
$('body').click(function(){
|
$('body').click(function () {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(window).resize(function() {
|
$(window).resize(function () {
|
||||||
// This will execute whenever the window is resized
|
// This will execute whenever the window is resized
|
||||||
if($(window).width() < 1000){
|
if ($(window).width() < 1000) {
|
||||||
$('.too_small').popover("show");
|
$('.too_small').popover("show");
|
||||||
document.getElementById('schedule_table_wrapper').hidden = true;
|
document.getElementById('schedule_table_wrapper').hidden = true;
|
||||||
document.getElementById('mini_schedule_table_wrapper').hidden = false;
|
document.getElementById('mini_schedule_table_wrapper').hidden = false;
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
document.getElementById('schedule_table_wrapper').hidden = false;
|
document.getElementById('schedule_table_wrapper').hidden = false;
|
||||||
document.getElementById('mini_schedule_table_wrapper').hidden = true;
|
document.getElementById('mini_schedule_table_wrapper').hidden = true;
|
||||||
} // New width
|
} // New width
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
return r ? r[1] : undefined;
|
return r ? r[1] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
console.log("ready!");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function yesnoCheck(that) {
|
||||||
|
if (that.value == "command") {
|
||||||
|
document.getElementById("ifYes").style.display = "block";
|
||||||
|
document.getElementById("command").required = true;
|
||||||
|
} else {
|
||||||
|
document.getElementById("ifYes").style.display = "none";
|
||||||
|
document.getElementById("command").required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$( document ).ready(function() {
|
|
||||||
console.log( "ready!" );
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function yesnoCheck(that) {
|
|
||||||
if (that.value == "command") {
|
|
||||||
document.getElementById("ifYes").style.display = "block";
|
|
||||||
document.getElementById("command").required = true;
|
|
||||||
} else {
|
|
||||||
document.getElementById("ifYes").style.display = "none";
|
|
||||||
document.getElementById("command").required = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function basicAdvanced(that) {
|
function basicAdvanced(that) {
|
||||||
if (that.value == "advanced") {
|
if (that.value == "advanced") {
|
||||||
document.getElementById("ifAdvanced").style.display = "block";
|
document.getElementById("ifAdvanced").style.display = "block";
|
||||||
document.getElementById("ifBasic").style.display = "none";
|
document.getElementById("ifBasic").style.display = "none";
|
||||||
document.getElementById("interval").required = false;
|
document.getElementById("interval").required = false;
|
||||||
document.getElementById("time").required = false;
|
document.getElementById("time").required = false;
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("ifAdvanced").style.display = "none";
|
document.getElementById("ifAdvanced").style.display = "none";
|
||||||
document.getElementById("ifBasic").style.display = "block";
|
document.getElementById("ifBasic").style.display = "block";
|
||||||
document.getElementById("interval").required = true;
|
document.getElementById("interval").required = true;
|
||||||
document.getElementById("time").required = true;
|
document.getElementById("time").required = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function ifDays(that) {
|
function ifDays(that) {
|
||||||
if (that.value == "days") {
|
if (that.value == "days") {
|
||||||
document.getElementById("ifDays").style.display = "block";
|
document.getElementById("ifDays").style.display = "block";
|
||||||
document.getElementById("time").required = true;
|
document.getElementById("time").required = true;
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("ifDays").style.display = "none";
|
document.getElementById("ifDays").style.display = "none";
|
||||||
document.getElementById("time").required = false;
|
document.getElementById("time").required = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$( ".del_button" ).click(function() {
|
$(".del_button").click(function () {
|
||||||
var sch_id = $(this).data('sch');
|
var sch_id = $(this).data('sch');
|
||||||
|
|
||||||
console.log(sch_id)
|
console.log(sch_id)
|
||||||
|
|
||||||
bootbox.confirm({
|
bootbox.confirm({
|
||||||
title: "{{ translate('serverSchedules', 'areYouSure', data['lang']) }}",
|
title: "{{ translate('serverSchedules', 'areYouSure', data['lang']) }}",
|
||||||
message: "{{ translate('serverSchedules', 'confirmDelete', data['lang']) }}",
|
message: "{{ translate('serverSchedules', 'confirmDelete', data['lang']) }}",
|
||||||
buttons: {
|
buttons: {
|
||||||
cancel: {
|
cancel: {
|
||||||
label: '<i class="fas fa-times"></i> {{ translate("serverSchedules", "cancel", data['lang']) }}'
|
label: '<i class="fas fa-times"></i> {{ translate("serverSchedules", "cancel", data['lang']) }}'
|
||||||
},
|
},
|
||||||
confirm: {
|
confirm: {
|
||||||
className: 'btn-outline-danger',
|
className: 'btn-outline-danger',
|
||||||
label: '<i class="fas fa-check"></i> {{ translate("serverSchedules", "confirm", data['lang']) }}'
|
label: '<i class="fas fa-check"></i> {{ translate("serverSchedules", "confirm", data['lang']) }}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: function (result) {
|
callback: function (result) {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
if (result == true) {
|
if (result == true) {
|
||||||
del_task(sch_id, serverId);
|
del_task(sch_id, serverId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
function del_task(sch_id, id){
|
function del_task(sch_id, id) {
|
||||||
var token = getCookie("_xsrf")
|
var token = getCookie("_xsrf")
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
headers: {'X-XSRFToken': token},
|
headers: { 'X-XSRFToken': token },
|
||||||
url: '/ajax/del_task?server_id='+id+'&schedule_id='+sch_id,
|
url: '/ajax/del_task?server_id=' + id + '&schedule_id=' + sch_id,
|
||||||
data: {
|
data: {
|
||||||
schedule_id: sch_id,
|
schedule_id: sch_id,
|
||||||
id: id
|
id: id
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function (data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -31,7 +31,13 @@
|
|||||||
<div class="col-sm-12 grid-margin">
|
<div class="col-sm-12 grid-margin">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
{% include "parts/server_controls_list.html %}
|
|
||||||
|
<span class="d-none d-sm-block">
|
||||||
|
{% include "parts/server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
<span class="d-block d-sm-none">
|
||||||
|
{% include "parts/m_server_controls_list.html %}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -60,12 +66,12 @@
|
|||||||
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
{% elif data['downloading'] %}
|
{% elif data['downloading'] %}
|
||||||
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
|
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
|
||||||
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
|
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
|
||||||
data['lang']) }}</button>
|
data['lang']) }}</button>
|
||||||
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
|
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
|
||||||
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
|
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
|
||||||
<button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button>
|
<button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button>
|
||||||
@ -130,16 +136,16 @@
|
|||||||
webSocket.on('update_button_status', function (updateButton) {
|
webSocket.on('update_button_status', function (updateButton) {
|
||||||
if (updateButton.isUpdating) {
|
if (updateButton.isUpdating) {
|
||||||
if (updateButton.server_id == serverId) {
|
if (updateButton.server_id == serverId) {
|
||||||
console.log(updateButton.isUpdating)
|
console.log(updateButton.isUpdating)
|
||||||
document.getElementById('control_buttons').innerHTML = '<button onclick="" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate("serverTerm", "updating", data["lang"]) }}</button><button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate("serverTerm", "restart", data["lang"]) %}</button><button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate("serverTerm", "stop", data["lang"]) }}</button>';
|
document.getElementById('control_buttons').innerHTML = '<button onclick="" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate("serverTerm", "updating", data["lang"]) }}</button><button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate("serverTerm", "restart", data["lang"]) %}</button><button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate("serverTerm", "stop", data["lang"]) }}</button>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (updateButton.server_id == serverId) {
|
if (updateButton.server_id == serverId) {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
document.getElementById('update_control_buttons').innerHTML = '<button onclick="send_command(serverId, "start_server");" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate("serverTerm", "start", data["lang"]) }}</button><button onclick="send_command(serverId, "restart_server");" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate("serverTerm", "restart", data["lang"]) %}</button><button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate("serverTerm", "stop", data["lang"]) }}</button>';
|
document.getElementById('update_control_buttons').innerHTML = '<button onclick="send_command(serverId, "start_server");" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate("serverTerm", "start", data["lang"]) }}</button><button onclick="send_command(serverId, "restart_server");" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate("serverTerm", "restart", data["lang"]) %}</button><button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate("serverTerm", "stop", data["lang"]) }}</button>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Convert running to lower case (example: 'True' converts to 'true') and
|
// Convert running to lower case (example: 'True' converts to 'true') and
|
||||||
@ -162,10 +168,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (webSocket) {
|
if (webSocket) {
|
||||||
webSocket.on('send_start_reload', function () {
|
webSocket.on('send_start_reload', function () {
|
||||||
location.reload()
|
location.reload()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//{% end %}
|
//{% end %}
|
||||||
|
|
||||||
function get_server_log() {
|
function get_server_log() {
|
||||||
|
@ -55,7 +55,11 @@
|
|||||||
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{% if data['query'] %}
|
||||||
|
<form action="/public/login?{{ data['query'] }}" method="post">
|
||||||
|
{% else %}
|
||||||
<form action="/public/login" method="post">
|
<form action="/public/login" method="post">
|
||||||
|
{% end %}
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
||||||
|
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