mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' of http://gitlab.com/crafty-controller/crafty-commander into dev
This commit is contained in:
commit
5adfc613d8
@ -117,7 +117,7 @@ win-dev-build:
|
||||
--paths .venv\Lib\site-packages
|
||||
--hidden-import cryptography
|
||||
--hidden-import cffi
|
||||
--collect-all apscheduler
|
||||
--hidden-import apscheduler
|
||||
--collect-all tzlocal
|
||||
--collect-all tzdata
|
||||
--collect-all pytz
|
||||
@ -158,7 +158,7 @@ win-prod-build:
|
||||
--paths .venv\Lib\site-packages
|
||||
--hidden-import cryptography
|
||||
--hidden-import cffi
|
||||
--collect-all apscheduler
|
||||
--hidden-import apscheduler
|
||||
--collect-all tzlocal
|
||||
--collect-all tzdata
|
||||
--collect-all pytz
|
||||
|
@ -18,6 +18,7 @@ from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
from app.classes.models.users import ApiKeys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -70,3 +71,7 @@ class Crafty_Perms_Controller:
|
||||
@staticmethod
|
||||
def add_server_creation(user_id):
|
||||
return crafty_permissions.add_server_creation(user_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys):
|
||||
return crafty_permissions.get_api_key_permissions_list(key)
|
||||
|
@ -31,10 +31,6 @@ class Management_Controller:
|
||||
def get_latest_hosts_stats():
|
||||
return management_helper.get_latest_hosts_stats()
|
||||
|
||||
@staticmethod
|
||||
def new_api_token():
|
||||
return management_helper.new_api_token()
|
||||
|
||||
#************************************************************************************************
|
||||
# Commands Methods
|
||||
#************************************************************************************************
|
||||
@ -44,13 +40,11 @@ class Management_Controller:
|
||||
|
||||
@staticmethod
|
||||
def send_command(user_id, server_id, remote_ip, command):
|
||||
|
||||
server_name = servers_helper.get_server_friendly_name(server_id)
|
||||
|
||||
# Example: Admin issued command start_server for server Survival
|
||||
management_helper.add_to_audit_log(user_id, "issued command {} for server {}".format(command, server_name),
|
||||
server_id, remote_ip)
|
||||
|
||||
management_helper.add_command(server_id, user_id, remote_ip, command)
|
||||
|
||||
@staticmethod
|
||||
|
@ -39,7 +39,9 @@ class Roles_Controller:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def update_role(role_id, role_data={}, permissions_mask="00000000"):
|
||||
def update_role(role_id: str, role_data = None, permissions_mask: str = "00000000"):
|
||||
if role_data is None:
|
||||
role_data = {}
|
||||
base_data = Roles_Controller.get_role_with_servers(role_id)
|
||||
up_data = {}
|
||||
added_servers = set()
|
||||
|
@ -14,7 +14,7 @@ from app.classes.shared.console import console
|
||||
|
||||
from app.classes.shared.main_models import db_helper
|
||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import users_helper, ApiKeys
|
||||
from app.classes.models.roles import roles_helper
|
||||
from app.classes.models.servers import servers_helper
|
||||
|
||||
@ -42,11 +42,6 @@ class Server_Perms_Controller:
|
||||
permissions_list = server_permissions.get_role_permissions_list(role_id)
|
||||
return permissions_list
|
||||
|
||||
@staticmethod
|
||||
def get_server_permissions_foruser(user_id, server_id):
|
||||
permissions_list = server_permissions.get_user_permissions_list(user_id, server_id)
|
||||
return permissions_list
|
||||
|
||||
@staticmethod
|
||||
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
||||
return server_permissions.add_role_server(server_id, role_id, rs_permissions)
|
||||
@ -78,8 +73,30 @@ class Server_Perms_Controller:
|
||||
return server_permissions.get_role_permissions_list(role_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user_id, server_id):
|
||||
return server_permissions.get_user_permissions_list(user_id, server_id)
|
||||
def get_user_id_permissions_list(user_id: str, server_id: str):
|
||||
return server_permissions.get_user_id_permissions_list(user_id, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_id_permissions_list(user_id: str, server_id: str):
|
||||
return server_permissions.get_user_id_permissions_list(user_id, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats_from_roles(user_id):
|
||||
|
@ -17,7 +17,7 @@ from app.classes.shared.console import console
|
||||
from app.classes.shared.main_models import db_helper
|
||||
from app.classes.models.servers import servers_helper
|
||||
from app.classes.models.roles import roles_helper
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import users_helper, ApiKeys
|
||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
||||
|
||||
from app.classes.shared.server import Server
|
||||
@ -81,6 +81,22 @@ class Servers_Controller:
|
||||
def get_all_servers_stats():
|
||||
return servers_helper.get_all_servers_stats()
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
|
||||
server_data = []
|
||||
authorized_servers = Servers_Controller.get_authorized_servers(api_key.user.user_id)
|
||||
|
||||
for s in authorized_servers:
|
||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
||||
key_permissions = server_permissions.get_api_key_permissions_list(api_key, s.get('server_id'))
|
||||
if Enum_Permissions_Server.Commands in key_permissions:
|
||||
user_command_permission = True
|
||||
else:
|
||||
user_command_permission = False
|
||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0],
|
||||
"user_command_permission": user_command_permission})
|
||||
return server_data
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats(user_id):
|
||||
server_data = []
|
||||
@ -88,12 +104,18 @@ class Servers_Controller:
|
||||
|
||||
for s in authorized_servers:
|
||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
||||
user_permissions = server_permissions.get_user_permissions_list(user_id, s.get('server_id'))
|
||||
# TODO
|
||||
user_permissions = server_permissions.get_user_id_permissions_list(user_id, s.get('server_id'))
|
||||
if Enum_Permissions_Server.Commands in user_permissions:
|
||||
user_command_permission = True
|
||||
else:
|
||||
user_command_permission = False
|
||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0], "user_command_permission":user_command_permission})
|
||||
server_data.append({
|
||||
'server_data': s,
|
||||
'stats': db_helper.return_rows(latest)[0],
|
||||
'user_command_permission': user_command_permission
|
||||
})
|
||||
|
||||
return server_data
|
||||
|
||||
@staticmethod
|
||||
@ -112,17 +134,20 @@ class Servers_Controller:
|
||||
return servers_helper.server_id_exists(server_id)
|
||||
|
||||
@staticmethod
|
||||
def server_id_authorized(serverId, user_id):
|
||||
authorized = 0
|
||||
def server_id_authorized(server_id_a, user_id):
|
||||
user_roles = users_helper.user_role_query(user_id)
|
||||
for role in user_roles:
|
||||
authorized = server_permissions.get_role_servers_from_role_id(role.role_id)
|
||||
for server_id_b in server_permissions.get_role_servers_from_role_id(role.role_id):
|
||||
if str(server_id_a) == str(server_id_b.server_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
#authorized = db_helper.return_rows(authorized)
|
||||
|
||||
if authorized.count() == 0:
|
||||
return False
|
||||
return True
|
||||
@staticmethod
|
||||
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
|
||||
# TODO
|
||||
return Servers_Controller.server_id_authorized(server_id, api_key.user.user_id)
|
||||
# There is no view server permission
|
||||
# permission_helper.both_have_perm(api_key)
|
||||
|
||||
@staticmethod
|
||||
def set_update(server_id, value):
|
||||
|
@ -2,6 +2,8 @@ import os
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
import yaml
|
||||
import asyncio
|
||||
import shutil
|
||||
@ -13,6 +15,7 @@ from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
|
||||
from app.classes.models.users import Users, users_helper
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
||||
from app.classes.models.management import management_helper
|
||||
|
||||
@ -31,10 +34,6 @@ class Users_Controller:
|
||||
def get_id_by_name(username):
|
||||
return users_helper.get_user_id_by_name(username)
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
return users_helper.get_user_by_api_token(token)
|
||||
|
||||
@staticmethod
|
||||
def get_user_lang_by_id(user_id):
|
||||
return users_helper.get_user_lang_by_id(user_id)
|
||||
@ -52,7 +51,11 @@ class Users_Controller:
|
||||
users_helper.set_support_path(user_id, support_path)
|
||||
|
||||
@staticmethod
|
||||
def update_user(user_id, user_data={}, user_crafty_data={}):
|
||||
def update_user(user_id: str, user_data=None, user_crafty_data=None):
|
||||
if user_crafty_data is None:
|
||||
user_crafty_data = {}
|
||||
if user_data is None:
|
||||
user_data = {}
|
||||
base_data = users_helper.get_user(user_id)
|
||||
up_data = {}
|
||||
added_roles = set()
|
||||
@ -64,9 +67,6 @@ class Users_Controller:
|
||||
elif key == "roles":
|
||||
added_roles = user_data['roles'].difference(base_data['roles'])
|
||||
removed_roles = base_data['roles'].difference(user_data['roles'])
|
||||
elif key == "regen_api":
|
||||
if user_data['regen_api']:
|
||||
up_data['api_token'] = management_helper.new_api_token()
|
||||
elif key == "password":
|
||||
if user_data['password'] is not None and user_data['password'] != "":
|
||||
up_data['password'] = helper.encode_pass(user_data['password'])
|
||||
@ -77,13 +77,12 @@ class Users_Controller:
|
||||
logger.debug("user: {} +role:{} -role:{}".format(user_data, added_roles, removed_roles))
|
||||
for role in added_roles:
|
||||
users_helper.get_or_create(user_id=user_id, role_id=role)
|
||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
||||
permissions_mask = user_crafty_data.get('permissions_mask', '000')
|
||||
|
||||
if 'server_quantity' in user_crafty_data:
|
||||
limit_server_creation = user_crafty_data['server_quantity'][
|
||||
Enum_Permissions_Crafty.Server_Creation.name]
|
||||
|
||||
for key in user_crafty_data:
|
||||
if key == "permissions_mask":
|
||||
permissions_mask = user_crafty_data['permissions_mask']
|
||||
if key == "server_quantity":
|
||||
limit_server_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Server_Creation.name]
|
||||
limit_user_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.User_Config.name]
|
||||
limit_role_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Roles_Config.name]
|
||||
else:
|
||||
@ -98,8 +97,8 @@ class Users_Controller:
|
||||
users_helper.update_user(user_id, up_data)
|
||||
|
||||
@staticmethod
|
||||
def add_user(username, password=None, email="default@example.com", api_token=None, enabled=True, superuser=False):
|
||||
return users_helper.add_user(username, password=password, email=email, api_token=api_token, enabled=enabled, superuser=superuser)
|
||||
def add_user(username, password=None, email="default@example.com", enabled: bool = True, superuser: bool = False):
|
||||
return users_helper.add_user(username, password=password, email=email, enabled=enabled, superuser=superuser)
|
||||
|
||||
@staticmethod
|
||||
def remove_user(user_id):
|
||||
@ -109,9 +108,19 @@ class Users_Controller:
|
||||
def user_id_exists(user_id):
|
||||
return users_helper.user_id_exists(user_id)
|
||||
|
||||
#************************************************************************************************
|
||||
@staticmethod
|
||||
def get_user_id_by_api_token(token: str) -> str:
|
||||
token_data = authentication.check_no_iat(token)
|
||||
return token_data['user_id']
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
_, user = authentication.check(token)
|
||||
return user
|
||||
|
||||
# ************************************************************************************************
|
||||
# User Roles Methods
|
||||
#************************************************************************************************
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_roles_id(user_id):
|
||||
@ -132,3 +141,29 @@ class Users_Controller:
|
||||
@staticmethod
|
||||
def user_role_query(user_id):
|
||||
return users_helper.user_role_query(user_id)
|
||||
|
||||
# ************************************************************************************************
|
||||
# Api Keys Methods
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_keys(user_id: str):
|
||||
return users_helper.get_user_api_keys(user_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_key(key_id: str):
|
||||
return users_helper.get_user_api_key(key_id)
|
||||
|
||||
@staticmethod
|
||||
def add_user_api_key(name: str, user_id: str, superuser: bool = False,
|
||||
server_permissions_mask: Optional[str] = None,
|
||||
crafty_permissions_mask: Optional[str] = None):
|
||||
return users_helper.add_user_api_key(name, user_id, superuser, server_permissions_mask, crafty_permissions_mask)
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_keys(user_id: str):
|
||||
return users_helper.delete_user_api_keys(user_id)
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_key(key_id: str):
|
||||
return users_helper.delete_user_api_key(key_id)
|
||||
|
@ -12,6 +12,7 @@ from app.classes.shared.console import console
|
||||
from app.classes.models.servers import Servers
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.models.server_permissions import server_permissions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -190,7 +191,6 @@ class ServerJars:
|
||||
except Exception as e:
|
||||
logger.error("Unable to save jar to {path} due to error:{error}".format(path=path, error=e))
|
||||
pass
|
||||
websocket_helper.broadcast('notification', "Executable download finished for server named: " + name)
|
||||
|
||||
|
||||
return False
|
||||
|
@ -5,7 +5,8 @@ import datetime
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.models.users import Users
|
||||
from app.classes.models.users import Users, ApiKeys
|
||||
from app.classes.shared.permission_helper import permission_helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
@ -191,4 +192,18 @@ class Permissions_Crafty:
|
||||
User_Crafty.save(user_crafty)
|
||||
return user_crafty.created_server
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys):
|
||||
user = key.user
|
||||
if user.superuser and key.superuser:
|
||||
return crafty_permissions.get_permissions_list()
|
||||
else:
|
||||
user_permissions_mask = crafty_permissions.get_crafty_permissions_mask(user.user_id)
|
||||
key_permissions_mask: str = key.crafty_permissions
|
||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
||||
permissions_list = crafty_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
|
||||
|
||||
crafty_permissions = Permissions_Crafty()
|
@ -9,6 +9,8 @@ from app.classes.shared.main_models import db_helper
|
||||
from app.classes.models.users import Users, users_helper
|
||||
from app.classes.models.servers import Servers, servers_helper
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.models.server_permissions import server_permissions
|
||||
import time
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -156,7 +158,7 @@ class helpers_management:
|
||||
@staticmethod
|
||||
def get_unactioned_commands():
|
||||
query = Commands.select().where(Commands.executed == 0)
|
||||
return db_helper.return_rows(query)
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def mark_command_complete(command_id=None):
|
||||
@ -181,7 +183,9 @@ class helpers_management:
|
||||
|
||||
audit_msg = "{} {}".format(str(user_data['username']).capitalize(), log_msg)
|
||||
|
||||
websocket_helper.broadcast('notification', audit_msg)
|
||||
server_users = server_permissions.get_server_user_list(server_id)
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user,'notification', audit_msg)
|
||||
|
||||
Audit_Log.insert({
|
||||
Audit_Log.user_name: user_data['username'],
|
||||
|
@ -7,7 +7,8 @@ from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.models.servers import Servers
|
||||
from app.classes.models.roles import Roles
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import User_Roles, users_helper, ApiKeys, Users
|
||||
from app.classes.shared.permission_helper import permission_helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
@ -78,10 +79,7 @@ class Permissions_Servers:
|
||||
|
||||
@staticmethod
|
||||
def has_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
||||
result = False
|
||||
if permission_mask[permission_tested.value] == '1':
|
||||
result = True
|
||||
return result
|
||||
return permission_mask[permission_tested.value] == '1'
|
||||
|
||||
@staticmethod
|
||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value):
|
||||
@ -94,6 +92,14 @@ class Permissions_Servers:
|
||||
def get_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
||||
return permission_mask[permission_tested.value]
|
||||
|
||||
@staticmethod
|
||||
def get_token_permissions(permissions_mask, api_permissions_mask):
|
||||
permissions_list = []
|
||||
for member in Enum_Permissions_Server.__members__.items():
|
||||
if permission_helper.both_have_perm(permissions_mask, api_permissions_mask, member[1]):
|
||||
permissions_list.append(member[1])
|
||||
return permissions_list
|
||||
|
||||
|
||||
#************************************************************************************************
|
||||
# Role_Servers Methods
|
||||
@ -146,7 +152,9 @@ class Permissions_Servers:
|
||||
Role_Servers.save(role_server)
|
||||
|
||||
@staticmethod
|
||||
def delete_roles_permissions(role_id, removed_servers={}):
|
||||
def delete_roles_permissions(role_id, removed_servers=None):
|
||||
if removed_servers is None:
|
||||
removed_servers = {}
|
||||
return Role_Servers.delete().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id.in_(removed_servers)).execute()
|
||||
|
||||
@staticmethod
|
||||
@ -155,21 +163,67 @@ class Permissions_Servers:
|
||||
return Role_Servers.delete().where(Role_Servers.server_id == server_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user_id, server_id):
|
||||
permissions_mask = ''
|
||||
permissions_list = []
|
||||
def get_user_id_permissions_mask(user_id, server_id: str):
|
||||
user = users_helper.get_user_model(user_id)
|
||||
return server_permissions.get_user_permissions_mask(user, server_id)
|
||||
|
||||
user = users_helper.get_user(user_id)
|
||||
if user['superuser'] == True:
|
||||
@staticmethod
|
||||
def get_user_permissions_mask(user: Users, server_id: str):
|
||||
if user.superuser:
|
||||
permissions_mask = '1' * len(server_permissions.get_permissions_list())
|
||||
else:
|
||||
roles_list = users_helper.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()
|
||||
permissions_mask = role_server[0].permissions
|
||||
return permissions_mask
|
||||
|
||||
@staticmethod
|
||||
def get_server_user_list(server_id):
|
||||
final_users = []
|
||||
server_roles = Role_Servers.select().where(Role_Servers.server_id == server_id)
|
||||
super_users = Users.select().where(Users.superuser == True)
|
||||
for role in server_roles:
|
||||
users = User_Roles.select().where(User_Roles.role_id == role.role_id)
|
||||
for user in users:
|
||||
if user.user_id.user_id not in final_users:
|
||||
final_users.append(user.user_id.user_id)
|
||||
for suser in super_users:
|
||||
if suser.user_id not in final_users:
|
||||
final_users.append(suser.user_id)
|
||||
return final_users
|
||||
|
||||
@staticmethod
|
||||
def get_user_id_permissions_list(user_id, server_id: str):
|
||||
user = users_helper.get_user_model(user_id)
|
||||
return server_permissions.get_user_permissions_list(user, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user: Users, server_id: str):
|
||||
if user.superuser:
|
||||
permissions_list = server_permissions.get_permissions_list()
|
||||
else:
|
||||
roles_list = users_helper.get_user_roles_id(user_id)
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == int(server_id)).execute()
|
||||
if len(role_server) > 0:
|
||||
permissions_mask = role_server[0].permissions
|
||||
else:
|
||||
permissions_mask = '00000000'
|
||||
permissions_mask = server_permissions.get_user_permissions_mask(user, server_id)
|
||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
server_permissions = Permissions_Servers()
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id, server_id: str):
|
||||
key = ApiKeys.get(ApiKeys.token_id == key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
user = key.user
|
||||
if user.superuser and key.superuser:
|
||||
return server_permissions.get_permissions_list()
|
||||
else:
|
||||
roles_list = users_helper.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()
|
||||
user_permissions_mask = role_server[0].permissions
|
||||
key_permissions_mask = key.server_permissions
|
||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
|
||||
server_permissions = Permissions_Servers()
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import datetime
|
||||
from typing import Optional, List, Union
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
@ -41,14 +42,32 @@ class Users(Model):
|
||||
email = CharField(default="default@example.com")
|
||||
enabled = BooleanField(default=True)
|
||||
superuser = BooleanField(default=False)
|
||||
api_token = CharField(default="", unique=True, index=True) # we may need to revisit this
|
||||
lang = CharField(default="en_EN")
|
||||
support_logs = CharField(default = '')
|
||||
valid_tokens_from = DateTimeField(default=datetime.datetime.now)
|
||||
|
||||
class Meta:
|
||||
table_name = "users"
|
||||
database = database
|
||||
|
||||
|
||||
# ************************************************************************************************
|
||||
# API Keys Class
|
||||
# ************************************************************************************************
|
||||
class ApiKeys(Model):
|
||||
token_id = AutoField()
|
||||
name = CharField(default='', unique=True, index=True)
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
user = ForeignKeyField(Users, backref='api_token', index=True)
|
||||
server_permissions = CharField(default='00000000')
|
||||
crafty_permissions = CharField(default='000')
|
||||
superuser = BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
table_name = 'api_keys'
|
||||
database = database
|
||||
|
||||
|
||||
#************************************************************************************************
|
||||
# User Roles Class
|
||||
#************************************************************************************************
|
||||
@ -86,18 +105,6 @@ class helper_users:
|
||||
except DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
query = Users.select().where(Users.api_token == token)
|
||||
|
||||
if query.exists():
|
||||
user = model_to_dict(Users.get(Users.api_token == token))
|
||||
# I know it should apply it without setting it but I'm just making sure
|
||||
user = users_helper.add_user_roles(user)
|
||||
return user
|
||||
else:
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def user_query(user_id):
|
||||
user_query = Users.select().where(Users.user_id == user_id)
|
||||
@ -117,7 +124,6 @@ class helper_users:
|
||||
'email': "default@example.com",
|
||||
'enabled': True,
|
||||
'superuser': True,
|
||||
'api_token': None,
|
||||
'roles': [],
|
||||
'servers': [],
|
||||
'support_logs': '',
|
||||
@ -140,21 +146,21 @@ class helper_users:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def add_user(username, password=None, email=None, api_token=None, enabled=True, superuser=False):
|
||||
def get_user_model(user_id: str) -> Users:
|
||||
user = Users.get(Users.user_id == user_id)
|
||||
user = users_helper.add_user_roles(user)
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
def add_user(username: str, password: Optional[str] = None, email: Optional[str] = None, enabled: bool = True, superuser: bool = False) -> str:
|
||||
if password is not None:
|
||||
pw_enc = helper.encode_pass(password)
|
||||
else:
|
||||
pw_enc = None
|
||||
if api_token is None:
|
||||
api_token = users_helper.new_api_token()
|
||||
else:
|
||||
if type(api_token) is not str and len(api_token) != 32:
|
||||
raise ValueError("API token must be a 32 character string")
|
||||
user_id = Users.insert({
|
||||
Users.username: username.lower(),
|
||||
Users.password: pw_enc,
|
||||
Users.email: email,
|
||||
Users.api_token: api_token,
|
||||
Users.enabled: enabled,
|
||||
Users.superuser: superuser,
|
||||
Users.created: helper.get_time_as_string()
|
||||
@ -162,10 +168,21 @@ class helper_users:
|
||||
return user_id
|
||||
|
||||
@staticmethod
|
||||
def update_user(user_id, up_data={}):
|
||||
def update_user(user_id, up_data=None):
|
||||
if up_data is None:
|
||||
up_data = {}
|
||||
if up_data:
|
||||
Users.update(up_data).where(Users.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_super_user_list():
|
||||
final_users = []
|
||||
super_users = Users.select().where(Users.superuser == True)
|
||||
for suser in super_users:
|
||||
if suser.user_id not in final_users:
|
||||
final_users.append(suser.user_id)
|
||||
return final_users
|
||||
|
||||
@staticmethod
|
||||
def remove_user(user_id):
|
||||
with database.atomic():
|
||||
@ -183,14 +200,6 @@ class helper_users:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def new_api_token():
|
||||
while True:
|
||||
token = helper.random_string_generator(32)
|
||||
test = list(Users.select(Users.user_id).where(Users.api_token == token))
|
||||
if len(test) == 0:
|
||||
return token
|
||||
|
||||
#************************************************************************************************
|
||||
# User_Roles Methods
|
||||
#************************************************************************************************
|
||||
@ -223,7 +232,7 @@ class helper_users:
|
||||
}).execute()
|
||||
|
||||
@staticmethod
|
||||
def add_user_roles(user):
|
||||
def add_user_roles(user: Union[dict, Users]):
|
||||
if type(user) == dict:
|
||||
user_id = user['user_id']
|
||||
else:
|
||||
@ -237,7 +246,11 @@ class helper_users:
|
||||
for r in roles_query:
|
||||
roles.add(r.role_id.role_id)
|
||||
|
||||
user['roles'] = roles
|
||||
if type(user) == dict:
|
||||
user['roles'] = roles
|
||||
else:
|
||||
user.roles = roles
|
||||
|
||||
#logger.debug("user: ({}) {}".format(user_id, user))
|
||||
return user
|
||||
|
||||
@ -257,5 +270,36 @@ class helper_users:
|
||||
def remove_roles_from_role_id(role_id):
|
||||
User_Roles.delete().where(User_Roles.role_id == role_id).execute()
|
||||
|
||||
# ************************************************************************************************
|
||||
# ApiKeys Methods
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_keys(user_id: str):
|
||||
return ApiKeys.select().where(ApiKeys.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_key(key_id: str) -> ApiKeys:
|
||||
return ApiKeys.get(ApiKeys.token_id == key_id)
|
||||
|
||||
@staticmethod
|
||||
def add_user_api_key(name: str, user_id: str, superuser: bool = False, server_permissions_mask: Optional[str] = None, crafty_permissions_mask: Optional[str] = None):
|
||||
return ApiKeys.insert({
|
||||
ApiKeys.name: name,
|
||||
ApiKeys.user_id: user_id,
|
||||
**({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
|
||||
def delete_user_api_keys(user_id: str):
|
||||
ApiKeys.delete().where(ApiKeys.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_key(key_id: str):
|
||||
ApiKeys.delete().where(ApiKeys.token_id == key_id).execute()
|
||||
|
||||
|
||||
|
||||
users_helper = helper_users()
|
76
app/classes/shared/authentication.py
Normal file
76
app/classes/shared/authentication.py
Normal file
@ -0,0 +1,76 @@
|
||||
import logging
|
||||
import time
|
||||
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.shared.helpers import helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Authentication:
|
||||
def __init__(self):
|
||||
self.secret = "my secret"
|
||||
self.secret = helper.get_setting('apikey_secret', None)
|
||||
|
||||
if self.secret is None or self.secret == 'random':
|
||||
self.secret = helper.random_string_generator(64)
|
||||
|
||||
@staticmethod
|
||||
def generate(user_id, extra=None):
|
||||
if extra is None:
|
||||
extra = {}
|
||||
return jwt.encode(
|
||||
{
|
||||
'user_id': user_id,
|
||||
'iat': int(time.time()),
|
||||
**extra
|
||||
},
|
||||
authentication.secret,
|
||||
algorithm="HS256"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def read(token):
|
||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
|
||||
@staticmethod
|
||||
def check_no_iat(token) -> Optional[Dict[str, Any]]:
|
||||
try:
|
||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
except PyJWTError as error:
|
||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check(token) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
||||
try:
|
||||
data = jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
except PyJWTError as error:
|
||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||
return None
|
||||
iat: int = data['iat']
|
||||
key: Optional[ApiKeys] = None
|
||||
if 'token_id' in data:
|
||||
key_id = data['token_id']
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
if key is None:
|
||||
return None
|
||||
user_id: str = data['user_id']
|
||||
user = users_helper.get_user(user_id)
|
||||
# TODO: Have a cache or something so we don't constantly have to query the database
|
||||
if int(user.get('valid_tokens_from').timestamp()) < iat:
|
||||
# Success!
|
||||
return key, data, user
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check_bool(token) -> bool:
|
||||
return authentication.check(token) is not None
|
||||
|
||||
|
||||
authentication = Authentication()
|
@ -41,29 +41,33 @@ class MainPrompt(cmd.Cmd, object):
|
||||
self.universal_exit()
|
||||
|
||||
def do_migrations(self, line):
|
||||
if (line == 'up'):
|
||||
if line == 'up':
|
||||
self.migration_manager.up()
|
||||
elif (line == 'down'):
|
||||
elif line == 'down':
|
||||
self.migration_manager.down()
|
||||
elif (line == 'done'):
|
||||
elif line == 'done':
|
||||
console.info(self.migration_manager.done)
|
||||
elif (line == 'todo'):
|
||||
elif line == 'todo':
|
||||
console.info(self.migration_manager.todo)
|
||||
elif (line == 'diff'):
|
||||
elif line == 'diff':
|
||||
console.info(self.migration_manager.diff)
|
||||
elif (line == 'info'):
|
||||
elif line == 'info':
|
||||
console.info('Done: {}'.format(self.migration_manager.done))
|
||||
console.info('FS: {}'.format(self.migration_manager.todo))
|
||||
console.info('Todo: {}'.format(self.migration_manager.diff))
|
||||
elif (line.startswith('add ')):
|
||||
elif line.startswith('add '):
|
||||
migration_name = line[len('add '):]
|
||||
self.migration_manager.create(migration_name, False)
|
||||
else:
|
||||
console.info('Unknown migration command')
|
||||
|
||||
def do_threads(self, line):
|
||||
|
||||
@staticmethod
|
||||
def do_threads(_line):
|
||||
for thread in threading.enumerate():
|
||||
print(f'Name: {thread.name} IDENT: {thread.ident}')
|
||||
if sys.version_info >= (3, 8):
|
||||
print(f'Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}')
|
||||
else:
|
||||
print(f'Name: {thread.name} Identifier: {thread.ident}')
|
||||
|
||||
def universal_exit(self):
|
||||
logger.info("Stopping all server daemons / threads")
|
||||
@ -75,11 +79,10 @@ class MainPrompt(cmd.Cmd, object):
|
||||
sys.exit(0)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def help_exit():
|
||||
console.help("Stops the server if running, Exits the program")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def help_migrations():
|
||||
console.help("Only for advanced users. Use with caution")
|
||||
|
@ -164,25 +164,6 @@ class Helpers:
|
||||
cmd_out[ci] += c
|
||||
return cmd_out
|
||||
|
||||
def check_for_old_logs(self, db_helper):
|
||||
servers = db_helper.get_all_defined_servers()
|
||||
for server in servers:
|
||||
logs_path = os.path.split(server['log_path'])[0]
|
||||
latest_log_file = os.path.split(server['log_path'])[1]
|
||||
logs_delete_after = int(server['logs_delete_after'])
|
||||
if logs_delete_after == 0:
|
||||
continue
|
||||
|
||||
log_files = list(filter(
|
||||
lambda val: val != latest_log_file,
|
||||
os.listdir(logs_path)
|
||||
))
|
||||
for log_file in log_files:
|
||||
log_file_path = os.path.join(logs_path, log_file)
|
||||
if self.check_file_exists(log_file_path) and \
|
||||
self.is_file_older_than_x_days(log_file_path, logs_delete_after):
|
||||
os.remove(log_file_path)
|
||||
|
||||
def get_setting(self, key, default_return=False):
|
||||
|
||||
try:
|
||||
@ -272,11 +253,13 @@ class Helpers:
|
||||
(r'(\[.+?/INFO\])', r'<span class="mc-log-info">\1</span>'),
|
||||
(r'(\[.+?/WARN\])', r'<span class="mc-log-warn">\1</span>'),
|
||||
(r'(\[.+?/ERROR\])', r'<span class="mc-log-error">\1</span>'),
|
||||
(r'(\[.+?/FATAL\])', r'<span class="mc-log-fatal">\1</span>'),
|
||||
(r'(\w+?\[/\d+?\.\d+?\.\d+?\.\d+?\:\d+?\])', r'<span class="mc-log-keyword">\1</span>'),
|
||||
(r'\[(\d\d:\d\d:\d\d)\]', r'<span class="mc-log-time">[\1]</span>'),
|
||||
(r'(\[.+? INFO\])', r'<span class="mc-log-info">\1</span>'),
|
||||
(r'(\[.+? WARN\])', r'<span class="mc-log-warn">\1</span>'),
|
||||
(r'(\[.+? ERROR\])', r'<span class="mc-log-error">\1</span>')
|
||||
(r'(\[.+? ERROR\])', r'<span class="mc-log-error">\1</span>'),
|
||||
(r'(\[.+? FATAL\])', r'<span class="mc-log-fatal">\1</span>')
|
||||
]
|
||||
|
||||
# highlight users keywords
|
||||
|
@ -3,6 +3,8 @@ import pathlib
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
from app.classes.models.server_permissions import Enum_Permissions_Server
|
||||
from app.classes.models.users import helper_users
|
||||
from peewee import DoesNotExist
|
||||
@ -18,10 +20,16 @@ from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
|
||||
#Importing Models
|
||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
||||
# Importing Models
|
||||
from app.classes.models.servers import servers_helper
|
||||
#Importing Controllers
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
|
||||
# Importing Controllers
|
||||
from app.classes.controllers.crafty_perms_controller import Crafty_Perms_Controller
|
||||
from app.classes.controllers.management_controller import Management_Controller
|
||||
from app.classes.controllers.users_controller import Users_Controller
|
||||
@ -29,11 +37,6 @@ from app.classes.controllers.roles_controller import Roles_Controller
|
||||
from app.classes.controllers.server_perms_controller import Server_Perms_Controller
|
||||
from app.classes.controllers.servers_controller import Servers_Controller
|
||||
|
||||
from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Controller:
|
||||
@ -134,7 +137,8 @@ class Controller:
|
||||
|
||||
|
||||
def package_support_logs(self, exec_user):
|
||||
time.sleep(5)
|
||||
#pausing so on screen notifications can run for user
|
||||
time.sleep(7)
|
||||
websocket_helper.broadcast_user(exec_user['user_id'], 'notification', 'Preparing your support logs')
|
||||
tempDir = tempfile.mkdtemp()
|
||||
tempZipStorage = tempfile.mkdtemp()
|
||||
@ -159,7 +163,10 @@ class Controller:
|
||||
for server in auth_servers:
|
||||
final_path = os.path.join(server_path, str(server['server_name']))
|
||||
os.mkdir(final_path)
|
||||
shutil.copy(server['log_path'], final_path)
|
||||
try:
|
||||
shutil.copy(server['log_path'], final_path)
|
||||
except Exception as e:
|
||||
logger.warning("Failed to copy file with error: {}".format(e))
|
||||
#Copy crafty logs to archive dir
|
||||
full_log_name = os.path.join(crafty_path, 'logs')
|
||||
shutil.copytree(os.path.join(self.project_root, 'logs'), full_log_name)
|
||||
@ -168,12 +175,12 @@ class Controller:
|
||||
tempZipStorage += '.zip'
|
||||
websocket_helper.broadcast_user(exec_user['user_id'], 'send_logs_bootbox', {
|
||||
})
|
||||
|
||||
|
||||
self.users.set_support_path(exec_user['user_id'], tempZipStorage)
|
||||
|
||||
@staticmethod
|
||||
def add_system_user():
|
||||
helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", helper_users.new_api_token(), False, False)
|
||||
helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", False, False)
|
||||
|
||||
def get_server_settings(self, server_id):
|
||||
for s in self.servers_list:
|
||||
@ -183,17 +190,17 @@ class Controller:
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
return False
|
||||
|
||||
def get_server_obj(self, server_id):
|
||||
def get_server_obj(self, server_id: Union[str, int]) -> Union[bool, Server]:
|
||||
for s in self.servers_list:
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if str(s['server_id']) == str(server_id):
|
||||
return s['server_obj']
|
||||
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
return False
|
||||
return False # TODO: Change to None
|
||||
|
||||
def get_server_data(self, server_id):
|
||||
def get_server_data(self, server_id: str):
|
||||
for s in self.servers_list:
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if str(s['server_id']) == str(server_id):
|
||||
return s['server_data_obj']
|
||||
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
@ -251,6 +258,11 @@ class Controller:
|
||||
server_id = helper.create_uuid()
|
||||
server_dir = os.path.join(helper.servers_dir, server_id)
|
||||
backup_path = os.path.join(helper.backup_path, server_id)
|
||||
if helper.is_os_windows():
|
||||
server_dir = helper.wtol_path(server_dir)
|
||||
backup_path = helper.wtol_path(backup_path)
|
||||
server_dir.replace(' ', '^ ')
|
||||
backup_path.replace(' ', '^ ')
|
||||
|
||||
server_file = "{server}-{version}.jar".format(server=server, version=version)
|
||||
full_jar_path = os.path.join(server_dir, server_file)
|
||||
@ -274,7 +286,12 @@ class Controller:
|
||||
logger.error("Unable to create required server files due to :{}".format(e))
|
||||
return False
|
||||
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
if helper.is_os_windows():
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar "{}" nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
else:
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
server_log_file = "{}/logs/latest.log".format(server_dir)
|
||||
@ -307,6 +324,11 @@ class Controller:
|
||||
server_id = helper.create_uuid()
|
||||
new_server_dir = os.path.join(helper.servers_dir, server_id)
|
||||
backup_path = os.path.join(helper.backup_path, server_id)
|
||||
if helper.is_os_windows():
|
||||
new_server_dir = helper.wtol_path(new_server_dir)
|
||||
backup_path = helper.wtol_path(backup_path)
|
||||
new_server_dir.replace(' ', '^ ')
|
||||
backup_path.replace(' ', '^ ')
|
||||
|
||||
helper.ensure_dir_exists(new_server_dir)
|
||||
helper.ensure_dir_exists(backup_path)
|
||||
@ -324,7 +346,13 @@ class Controller:
|
||||
f.close()
|
||||
|
||||
full_jar_path = os.path.join(new_server_dir, server_jar)
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
|
||||
if helper.is_os_windows():
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
'"'+full_jar_path+'"')
|
||||
else:
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
server_log_file = "{}/logs/latest.log".format(new_server_dir)
|
||||
@ -338,6 +366,12 @@ class Controller:
|
||||
server_id = helper.create_uuid()
|
||||
new_server_dir = os.path.join(helper.servers_dir, server_id)
|
||||
backup_path = os.path.join(helper.backup_path, server_id)
|
||||
if helper.is_os_windows():
|
||||
new_server_dir = helper.wtol_path(new_server_dir)
|
||||
backup_path = helper.wtol_path(backup_path)
|
||||
new_server_dir.replace(' ', '^ ')
|
||||
backup_path.replace(' ', '^ ')
|
||||
|
||||
tempDir = helper.get_os_understandable_path(zip_path)
|
||||
helper.ensure_dir_exists(new_server_dir)
|
||||
helper.ensure_dir_exists(backup_path)
|
||||
@ -357,7 +391,13 @@ class Controller:
|
||||
f.close()
|
||||
|
||||
full_jar_path = os.path.join(new_server_dir, server_jar)
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
|
||||
if helper.is_os_windows():
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
'"'+full_jar_path+'"')
|
||||
else:
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
logger.debug('command: ' + server_command)
|
||||
@ -373,6 +413,9 @@ class Controller:
|
||||
old_bu_path = server_data['backup_path']
|
||||
Server_Perms_Controller.backup_role_swap(old_server_id, new_server_id)
|
||||
backup_path = helper.validate_traversal(helper.backup_path, old_bu_path)
|
||||
if helper.is_os_windows():
|
||||
backup_path = helper.wtol_path(backup_path)
|
||||
backup_path.replace(' ', '^ ')
|
||||
backup_path_components = list(backup_path.parts)
|
||||
backup_path_components[-1] = new_uuid
|
||||
new_bu_path = pathlib.PurePath(os.path.join(*backup_path_components))
|
||||
@ -383,6 +426,7 @@ class Controller:
|
||||
|
||||
def register_server(self, name: str, server_uuid: str, server_dir: str, backup_path: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port: int):
|
||||
# put data in the db
|
||||
|
||||
new_id = self.servers.create_server(name, server_uuid, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, server_port)
|
||||
if not helper.check_file_exists(os.path.join(server_dir, "crafty_managed.txt")):
|
||||
try:
|
||||
@ -406,7 +450,7 @@ class Controller:
|
||||
for s in self.servers_list:
|
||||
|
||||
# if this is the droid... im mean server we are looking for...
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if str(s['server_id']) == str(server_id):
|
||||
server_data = self.get_server_data(server_id)
|
||||
server_name = server_data['server_name']
|
||||
backup_dir = self.servers.get_server_data_by_id(server_id)['backup_path']
|
||||
@ -427,7 +471,7 @@ class Controller:
|
||||
if helper.check_path_exists(self.servers.get_server_data_by_id(server_id)['backup_path']):
|
||||
shutil.rmtree(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['backup_path']))
|
||||
|
||||
|
||||
|
||||
#Cleanup scheduled tasks
|
||||
try:
|
||||
helpers_management.delete_scheduled_task_by_server(server_id)
|
||||
|
@ -10,6 +10,10 @@ from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
|
||||
|
||||
# To disable warning about unused import ; Users is imported from here in other places
|
||||
Users = Users
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
peewee_logger.setLevel(logging.INFO)
|
||||
@ -39,20 +43,16 @@ class db_builder:
|
||||
|
||||
username = default_data.get("username", 'admin')
|
||||
password = default_data.get("password", 'crafty')
|
||||
#api_token = helper.random_string_generator(32)
|
||||
#
|
||||
#Users.insert({
|
||||
# Users.username: username.lower(),
|
||||
# Users.password: helper.encode_pass(password),
|
||||
# Users.api_token: api_token,
|
||||
# Users.enabled: True,
|
||||
# Users.superuser: True
|
||||
#}).execute()
|
||||
user_id = users_helper.add_user(username=username, password=password, email="default@example.com", superuser=True)
|
||||
#users_helper.update_user(user_id, user_crafty_data={"permissions_mask":"111", "server_quantity":[-1,-1,-1]} )
|
||||
|
||||
#console.info("API token is {}".format(api_token))
|
||||
|
||||
@staticmethod
|
||||
def is_fresh_install():
|
||||
try:
|
||||
|
@ -4,13 +4,9 @@ import typing as t
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from importlib import import_module
|
||||
from functools import wraps
|
||||
|
||||
try:
|
||||
from functools import cached_property
|
||||
except ImportError:
|
||||
from cached_property import cached_property
|
||||
from functools import cached_property
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
@ -21,7 +17,7 @@ try:
|
||||
import peewee
|
||||
from playhouse.migrate import (
|
||||
SchemaMigrator as ScM,
|
||||
SqliteMigrator as SqM,
|
||||
SqliteMigrator,
|
||||
Operation, SQL, operation, SqliteDatabase,
|
||||
make_index_name, Context
|
||||
)
|
||||
@ -32,6 +28,22 @@ except ModuleNotFoundError as e:
|
||||
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||
sys.exit(1)
|
||||
|
||||
MIGRATE_TABLE = 'migratehistory'
|
||||
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
||||
import peewee
|
||||
|
||||
def migrate(migrator, db):
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
{migrate}
|
||||
|
||||
def rollback(migrator, db):
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
||||
{rollback}'''
|
||||
|
||||
|
||||
class MigrateHistory(peewee.Model):
|
||||
"""
|
||||
@ -41,30 +53,15 @@ class MigrateHistory(peewee.Model):
|
||||
name = peewee.CharField(unique=True)
|
||||
migrated_at = peewee.DateTimeField(default=datetime.utcnow)
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
def __unicode__(self) -> str:
|
||||
"""
|
||||
String representation of this migration
|
||||
"""
|
||||
return self.name
|
||||
|
||||
|
||||
MIGRATE_TABLE = 'migratehistory'
|
||||
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
||||
|
||||
|
||||
def migrate(migrator, database, **kwargs):
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
{migrate}
|
||||
|
||||
|
||||
def rollback(migrator, database, **kwargs):
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
||||
{rollback}'''
|
||||
VOID: t.Callable = lambda m, d: None
|
||||
class Meta:
|
||||
table_name = MIGRATE_TABLE
|
||||
|
||||
|
||||
def get_model(method):
|
||||
@ -75,11 +72,12 @@ def get_model(method):
|
||||
@wraps(method)
|
||||
def wrapper(migrator, model, *args, **kwargs):
|
||||
if isinstance(model, str):
|
||||
return method(migrator, migrator.orm[model], *args, **kwargs)
|
||||
return method(migrator, migrator.table_dict[model], *args, **kwargs)
|
||||
return method(migrator, model, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class Migrator(object):
|
||||
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
|
||||
"""
|
||||
@ -88,8 +86,8 @@ class Migrator(object):
|
||||
if isinstance(database, peewee.Proxy):
|
||||
database = database.obj
|
||||
self.database: SqliteDatabase = database
|
||||
self.orm: t.Dict[str, peewee.Model] = {}
|
||||
self.operations: t.List[Operation] = []
|
||||
self.table_dict: t.Dict[str, peewee.Model] = {}
|
||||
self.operations: t.List[t.Union[Operation, callable]] = []
|
||||
self.migrator = SqliteMigrator(database)
|
||||
|
||||
def run(self):
|
||||
@ -113,13 +111,13 @@ class Migrator(object):
|
||||
"""
|
||||
Executes raw SQL.
|
||||
"""
|
||||
self.operations.append(self.migrator.sql(sql, *params))
|
||||
self.operations.append(SQL(sql, *params))
|
||||
|
||||
def create_table(self, model: peewee.Model) -> peewee.Model:
|
||||
"""
|
||||
Creates model and table in database.
|
||||
"""
|
||||
self.orm[model._meta.table_name] = model
|
||||
self.table_dict[model._meta.table_name] = model
|
||||
model._meta.database = self.database
|
||||
self.operations.append(model.create_table)
|
||||
return model
|
||||
@ -129,8 +127,8 @@ class Migrator(object):
|
||||
"""
|
||||
Drops model and table from database.
|
||||
"""
|
||||
del self.orm[model._meta.table_name]
|
||||
self.operations.append(self.migrator.drop_table(model))
|
||||
del self.table_dict[model._meta.table_name]
|
||||
self.operations.append(lambda: model.drop_table(cascade=False))
|
||||
|
||||
@get_model
|
||||
def add_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
|
||||
@ -147,64 +145,16 @@ class Migrator(object):
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def change_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
|
||||
"""
|
||||
Changes fields.
|
||||
"""
|
||||
for name, field in fields.items():
|
||||
old_field = model._meta.fields.get(name, field)
|
||||
old_column_name = old_field and old_field.column_name
|
||||
|
||||
model._meta.add_field(name, field)
|
||||
|
||||
if isinstance(old_field, peewee.ForeignKeyField):
|
||||
self.operations.append(self.migrator.drop_foreign_key_constraint(
|
||||
model._meta.table_name, old_column_name))
|
||||
|
||||
if old_column_name != field.column_name:
|
||||
self.operations.append(
|
||||
self.migrator.rename_column(
|
||||
model._meta.table_name, old_column_name, field.column_name))
|
||||
|
||||
if isinstance(field, peewee.ForeignKeyField):
|
||||
on_delete = field.on_delete if field.on_delete else 'RESTRICT'
|
||||
on_update = field.on_update if field.on_update else 'RESTRICT'
|
||||
self.operations.append(self.migrator.add_foreign_key_constraint(
|
||||
model._meta.table_name, field.column_name,
|
||||
field.rel_model._meta.table_name, field.rel_field.name,
|
||||
on_delete, on_update))
|
||||
continue
|
||||
|
||||
self.operations.append(self.migrator.change_column(
|
||||
model._meta.table_name, field.column_name, field))
|
||||
|
||||
if field.unique == old_field.unique:
|
||||
continue
|
||||
|
||||
if field.unique:
|
||||
index = (field.column_name,), field.unique
|
||||
self.operations.append(self.migrator.add_index(
|
||||
model._meta.table_name, *index))
|
||||
model._meta.indexes.append(index)
|
||||
else:
|
||||
index = (field.column_name,), old_field.unique
|
||||
self.operations.append(self.migrator.drop_index(
|
||||
model._meta.table_name, *index))
|
||||
model._meta.indexes.remove(index)
|
||||
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def drop_columns(self, model: peewee.Model, names: str, **kwargs) -> peewee.Model:
|
||||
def drop_columns(self, model: peewee.Model, names: str) -> peewee.Model:
|
||||
"""
|
||||
Removes fields from model.
|
||||
"""
|
||||
fields = [field for field in model._meta.fields.values()
|
||||
if field.name in names]
|
||||
cascade = kwargs.pop('cascade', True)
|
||||
for field in fields:
|
||||
self.__del_field__(model, field)
|
||||
if field.unique:
|
||||
# Drop unique index
|
||||
index_name = make_index_name(
|
||||
model._meta.table_name, [field.column_name])
|
||||
self.operations.append(self.migrator.drop_index(
|
||||
@ -250,16 +200,15 @@ class Migrator(object):
|
||||
Renames table in database.
|
||||
"""
|
||||
old_name = model._meta.table_name
|
||||
del self.orm[model._meta.table_name]
|
||||
del self.table_dict[model._meta.table_name]
|
||||
model._meta.table_name = new_name
|
||||
self.orm[model._meta.table_name] = model
|
||||
self.table_dict[model._meta.table_name] = model
|
||||
self.operations.append(self.migrator.rename_table(old_name, new_name))
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def add_index(self, model: peewee.Model, *columns: str, **kwargs) -> peewee.Model:
|
||||
def add_index(self, model: peewee.Model, *columns: str, unique=False) -> peewee.Model:
|
||||
"""Create indexes."""
|
||||
unique = kwargs.pop('unique', False)
|
||||
model._meta.indexes.append((columns, unique))
|
||||
columns_ = []
|
||||
for col in columns:
|
||||
@ -329,42 +278,8 @@ class Migrator(object):
|
||||
return model
|
||||
|
||||
|
||||
class SqliteMigrator(SqM):
|
||||
def drop_table(self, model):
|
||||
return lambda: model.drop_table(cascade=False)
|
||||
|
||||
@operation
|
||||
def change_column(self, table: str, column_name: str, field: peewee.Field):
|
||||
operations = [self.alter_change_column(table, column_name, field)]
|
||||
if not field.null:
|
||||
operations.extend([self.add_not_null(table, column_name)])
|
||||
return operations
|
||||
|
||||
def alter_change_column(self, table: str, column_name: str, field: peewee.Field) -> Operation:
|
||||
return self._update_column(table, column_name, lambda x, y: y)
|
||||
|
||||
@operation
|
||||
def sql(self, sql: str, *params) -> SQL:
|
||||
"""
|
||||
Executes raw SQL.
|
||||
"""
|
||||
return SQL(sql, *params)
|
||||
|
||||
def alter_add_column(
|
||||
self, table: str, column_name: str, field: peewee.Field, **kwargs) -> Operation:
|
||||
"""
|
||||
Fixes field name for ForeignKeys.
|
||||
"""
|
||||
name = field.name
|
||||
op = super().alter_add_column(
|
||||
table, column_name, field, **kwargs)
|
||||
if isinstance(field, peewee.ForeignKeyField):
|
||||
field.name = name
|
||||
return op
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class MigrationManager(object):
|
||||
|
||||
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
|
||||
|
||||
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
|
||||
@ -376,7 +291,7 @@ class MigrationManager(object):
|
||||
self.database = database
|
||||
|
||||
@cached_property
|
||||
def model(self) -> peewee.Model:
|
||||
def model(self) -> t.Type[MigrateHistory]:
|
||||
"""
|
||||
Initialize and cache the MigrationHistory model.
|
||||
"""
|
||||
@ -487,7 +402,7 @@ class MigrationManager(object):
|
||||
scope = {}
|
||||
code = compile(code, '<string>', 'exec', dont_inherit=True)
|
||||
exec(code, scope, None)
|
||||
return scope.get('migrate', VOID), scope.get('rollback', VOID)
|
||||
return scope.get('migrate', lambda m, d: None), scope.get('rollback', lambda m, d: None)
|
||||
|
||||
def up_one(self, name: str, migrator: Migrator,
|
||||
fake: bool = False, rollback: bool = False) -> str:
|
||||
@ -518,11 +433,11 @@ class MigrationManager(object):
|
||||
|
||||
except Exception:
|
||||
self.database.rollback()
|
||||
operation = 'Rollback' if rollback else 'Migration'
|
||||
logger.exception('{} failed: {}'.format(operation, name))
|
||||
operation_name = 'Rollback' if rollback else 'Migration'
|
||||
logger.exception('{} failed: {}'.format(operation_name, name))
|
||||
raise
|
||||
|
||||
def down(self, name: t.Optional[str] = None):
|
||||
def down(self):
|
||||
"""
|
||||
Rolls back migrations.
|
||||
"""
|
||||
|
23
app/classes/shared/permission_helper.py
Normal file
23
app/classes/shared/permission_helper.py
Normal file
@ -0,0 +1,23 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class PermissionHelper:
|
||||
@staticmethod
|
||||
def both_have_perm(a: str, b: str, permission_tested: Enum):
|
||||
return permission_helper.combine_perm_bool(a[permission_tested.value], b[permission_tested.value])
|
||||
|
||||
@staticmethod
|
||||
def combine_perm(a: str, b: str) -> str:
|
||||
return '1' if (a == '1' and b == '1') else '0'
|
||||
|
||||
@staticmethod
|
||||
def combine_perm_bool(a: str, b: str) -> bool:
|
||||
return a == '1' and b == '1'
|
||||
|
||||
@staticmethod
|
||||
def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str:
|
||||
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))
|
||||
|
||||
|
||||
permission_helper = PermissionHelper()
|
@ -25,6 +25,7 @@ from app.classes.models.management import management_helper
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.shared.translation import translation
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.server_permissions import server_permissions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -119,6 +120,8 @@ class Server:
|
||||
self.restart_count = 0
|
||||
self.crash_watcher_schedule = None
|
||||
self.stats = stats
|
||||
tz = get_localzone()
|
||||
self.server_scheduler = BackgroundScheduler(timezone=str(tz))
|
||||
self.backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name=f"backup_{self.name}")
|
||||
self.is_backingup = False
|
||||
|
||||
@ -144,8 +147,6 @@ class Server:
|
||||
logger.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
|
||||
console.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
|
||||
|
||||
tz = get_localzone()
|
||||
self.server_scheduler = BackgroundScheduler(timezone=str(tz))
|
||||
self.server_scheduler.add_job(self.run_scheduled_server, 'interval', seconds=delay, id=str(self.server_id))
|
||||
self.server_scheduler.start()
|
||||
|
||||
@ -289,8 +290,15 @@ class Server:
|
||||
websocket_helper.broadcast_user(user_id, 'send_start_error', {
|
||||
'error': translation.translate('error', 'portReminder', user_lang).format(self.name, loc_server_port)
|
||||
})
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
if user != user_id:
|
||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
||||
})
|
||||
else:
|
||||
websocket_helper.broadcast_user(user_id, 'send_start_reload', {
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
||||
})
|
||||
else:
|
||||
logger.warning("Server PID {} died right after starting - is this a server config issue?".format(self.process.pid))
|
||||
@ -300,7 +308,7 @@ class Server:
|
||||
logger.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
|
||||
console.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
|
||||
|
||||
self.crash_watcher_schedule = schedule.every(30).seconds.do(self.detect_crash).tag(self.name)
|
||||
self.crash_watcher_schedule = self.server_scheduler.add_job(self.detect_crash, 'interval', seconds=30, id="crash_watcher")
|
||||
|
||||
def check_internet_thread(self, user_id, user_lang):
|
||||
if user_id:
|
||||
@ -352,9 +360,14 @@ class Server:
|
||||
|
||||
# massive resetting of variables
|
||||
self.cleanup_server_object()
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
|
||||
self.stats.record_stats()
|
||||
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
||||
})
|
||||
|
||||
def restart_threaded_server(self, user_id):
|
||||
# if not already running, let's just start
|
||||
if not self.check_running():
|
||||
@ -383,11 +396,10 @@ class Server:
|
||||
return False
|
||||
|
||||
def send_command(self, command):
|
||||
console.info("COMMAND TIME: {}".format(command))
|
||||
if not self.check_running() and command.lower() != 'start':
|
||||
logger.warning("Server not running, unable to send command \"{}\"".format(command))
|
||||
return False
|
||||
|
||||
console.info("COMMAND TIME: {}".format(command))
|
||||
logger.debug("Sending command {} to server".format(command))
|
||||
|
||||
# send it
|
||||
@ -477,7 +489,7 @@ class Server:
|
||||
def remove_watcher_thread(self):
|
||||
logger.info("Removing old crash detection watcher thread")
|
||||
console.info("Removing old crash detection watcher thread")
|
||||
schedule.clear(self.name)
|
||||
self.crash_watcher_schedule.remove(self.server_name)
|
||||
|
||||
def agree_eula(self, user_id):
|
||||
file = os.path.join(self.server_path, 'eula.txt')
|
||||
@ -610,7 +622,9 @@ class Server:
|
||||
if len(websocket_helper.clients) > 0:
|
||||
# There are clients
|
||||
self.check_update()
|
||||
websocket_helper.broadcast('notification', "Executable update finished for " + self.name)
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for " + self.name)
|
||||
time.sleep(3)
|
||||
websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', {
|
||||
'isUpdating': self.check_update(),
|
||||
@ -619,15 +633,18 @@ class Server:
|
||||
})
|
||||
websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', {
|
||||
})
|
||||
websocket_helper.broadcast('notification', "Executable update finished for "+self.name)
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for "+self.name)
|
||||
|
||||
management_helper.add_to_audit_log_raw('Alert', '-1', self.server_id, "Executable update finished for "+self.name, self.settings['server_ip'])
|
||||
if wasStarted:
|
||||
self.start_server()
|
||||
elif not downloaded and not self.is_backingup:
|
||||
time.sleep(5)
|
||||
servers_helper.set_update(self.server_id, False)
|
||||
websocket_helper.broadcast('notification',
|
||||
server_users = server_permissions.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
websocket_helper.broadcast_user(user,'notification',
|
||||
"Executable update failed for " + self.name + ". Check log file for details.")
|
||||
logger.error("Executable download failed.")
|
||||
pass
|
||||
|
@ -1,4 +1,5 @@
|
||||
from datetime import timedelta
|
||||
from http import server
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
@ -25,6 +26,7 @@ logger = logging.getLogger('apscheduler')
|
||||
|
||||
try:
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
except ModuleNotFoundError as e:
|
||||
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||
@ -51,8 +53,8 @@ class TasksManager:
|
||||
self.controller = controller
|
||||
self.tornado = Webserver(controller, self)
|
||||
|
||||
tz = get_localzone()
|
||||
self.scheduler = BackgroundScheduler(timezone=str(tz))
|
||||
self.tz = get_localzone()
|
||||
self.scheduler = BackgroundScheduler(timezone=str(self.tz))
|
||||
|
||||
self.users_controller = Users_Controller()
|
||||
|
||||
@ -79,16 +81,20 @@ class TasksManager:
|
||||
logger.info("Reload from DB called. Current enabled schedules: ")
|
||||
for item in jobs:
|
||||
logger.info("JOB: {}".format(item))
|
||||
|
||||
|
||||
def command_watcher(self):
|
||||
while True:
|
||||
# select any commands waiting to be processed
|
||||
commands = management_helper.get_unactioned_commands()
|
||||
for c in commands:
|
||||
|
||||
svr = self.controller.get_server_obj(c['server_id']['server_id'])
|
||||
user_id = c.get('user')['user_id']
|
||||
command = c.get('command', None)
|
||||
try:
|
||||
svr = self.controller.get_server_obj(c.server_id)
|
||||
except:
|
||||
logger.error("Server value requested does note exist purging item from waiting commands.")
|
||||
management_helper.mark_command_complete(c.command_id)
|
||||
|
||||
user_id = c.user_id
|
||||
command = c.command
|
||||
|
||||
if command == 'start_server':
|
||||
svr.run_threaded_server(user_id)
|
||||
@ -106,7 +112,7 @@ class TasksManager:
|
||||
svr.jar_update()
|
||||
else:
|
||||
svr.send_command(command)
|
||||
management_helper.mark_command_complete(c.get('command_id', None))
|
||||
management_helper.mark_command_complete(c.command_id)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
@ -153,11 +159,19 @@ class TasksManager:
|
||||
schedules = management_helper.get_schedules_enabled()
|
||||
self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED)
|
||||
#self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1')
|
||||
|
||||
#load schedules from DB
|
||||
for schedule in schedules:
|
||||
if schedule.cron_string != "":
|
||||
cron = schedule.cron_string.split(' ')
|
||||
self.scheduler.add_job(management_helper.add_command, 'cron', minute = cron[0], hour = cron[1], day = cron[2], month = cron[3], day_of_week = cron[4], id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
|
||||
try:
|
||||
self.scheduler.add_job(management_helper.add_command, CronTrigger.from_crontab(schedule.cron_string, timezone=str(self.tz)), id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
|
||||
except Exception as e:
|
||||
console.error("Failed to schedule task with error: {}.".format(e))
|
||||
console.warning("Removing failed task from DB.")
|
||||
logger.error("Failed to schedule task with error: {}.".format(e))
|
||||
logger.warning("Removing failed task from DB.")
|
||||
#remove items from DB if task fails to add to apscheduler
|
||||
management_helper.delete_scheduled_task(schedule.schedule_id)
|
||||
else:
|
||||
if schedule.interval_type == 'hours':
|
||||
self.scheduler.add_job(management_helper.add_command, 'cron', minute = 0, hour = '*/'+str(schedule.interval), id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
|
||||
@ -177,12 +191,14 @@ class TasksManager:
|
||||
sch_id = management_helper.create_scheduled_task(job_data['server_id'], job_data['action'], job_data['interval'], job_data['interval_type'], job_data['start_time'], job_data['command'], "None", job_data['enabled'], job_data['one_time'], job_data['cron_string'])
|
||||
if job_data['enabled']:
|
||||
if job_data['cron_string'] != "":
|
||||
cron = job_data['cron_string'].split(' ')
|
||||
try:
|
||||
self.scheduler.add_job(management_helper.add_command, 'cron', minute = cron[0], hour = cron[1], day = cron[2], month = cron[3], day_of_week = cron[4], id=str(sch_id), args=[job_data['server_id'], self.users_controller.get_id_by_name('system'), '127.0.0.1', job_data['command']])
|
||||
self.scheduler.add_job(management_helper.add_command, CronTrigger.from_crontab(job_data['cron_string'], timezone=str(self.tz)), id=str(sch_id), args=[job_data['server_id'], self.users_controller.get_id_by_name('system'), '127.0.0.1', job_data['command']])
|
||||
except Exception as e:
|
||||
console.error("Failed to schedule task with error: {}.".format(e))
|
||||
console.info("Removing failed task from DB.")
|
||||
console.warning("Removing failed task from DB.")
|
||||
logger.error("Failed to schedule task with error: {}.".format(e))
|
||||
logger.warning("Removing failed task from DB.")
|
||||
#remove items from DB if task fails to add to apscheduler
|
||||
management_helper.delete_scheduled_task(sch_id)
|
||||
else:
|
||||
if job_data['interval_type'] == 'hours':
|
||||
@ -197,13 +213,19 @@ class TasksManager:
|
||||
for item in jobs:
|
||||
logger.info("JOB: {}".format(item))
|
||||
|
||||
def remove_all_server_tasks(self, server_id):
|
||||
schedules = management_helper.get_schedules_by_server(server_id)
|
||||
for schedule in schedules:
|
||||
self.remove_job(schedule.schedule_id)
|
||||
|
||||
def remove_job(self, sch_id):
|
||||
job = management_helper.get_scheduled_task_model(sch_id)
|
||||
management_helper.delete_scheduled_task(sch_id)
|
||||
if job.enabled:
|
||||
self.scheduler.remove_job(str(sch_id))
|
||||
logger.info("Job with ID {} was deleted.".format(sch_id))
|
||||
else:
|
||||
logger.info("Job with ID {} was deleted from DB, but was not enabled. Not going to try removing something that doesn't exist from active schedules.")
|
||||
logger.info("Job with ID {} was deleted from DB, but was not enabled. Not going to try removing something that doesn't exist from active schedules.".format(sch_id))
|
||||
|
||||
def update_job(self, sch_id, job_data):
|
||||
management_helper.update_scheduled_task(sch_id, job_data)
|
||||
@ -264,8 +286,7 @@ class TasksManager:
|
||||
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")
|
||||
|
||||
@staticmethod
|
||||
def realtime():
|
||||
def realtime(self):
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
@ -290,7 +311,69 @@ class TasksManager:
|
||||
'mem_percent': host_stats.get('mem_percent'),
|
||||
'mem_usage': host_stats.get('mem_usage')
|
||||
})
|
||||
time.sleep(4)
|
||||
|
||||
servers = self.controller.servers_list
|
||||
servers_ping = []
|
||||
for srv in servers:
|
||||
server_data = srv.get('server_data_obj', False)
|
||||
if server_data:
|
||||
server_id = server_data.get('server_id', False)
|
||||
srv['raw_ping_result'] = self.controller.stats.get_raw_server_stats(server_id)
|
||||
if ("{}".format(srv['raw_ping_result'].get('icon')) == "b''"):
|
||||
srv['raw_ping_result']['icon'] = False
|
||||
|
||||
servers_ping.append({
|
||||
'id': srv['raw_ping_result'].get('id'),
|
||||
'started': srv['raw_ping_result'].get('started'),
|
||||
'running': srv['raw_ping_result'].get('running'),
|
||||
'cpu': srv['raw_ping_result'].get('cpu'),
|
||||
'mem': srv['raw_ping_result'].get('mem'),
|
||||
'mem_percent': srv['raw_ping_result'].get('mem_percent'),
|
||||
'world_name': srv['raw_ping_result'].get('world_name'),
|
||||
'world_size': srv['raw_ping_result'].get('world_size'),
|
||||
'server_port': srv['raw_ping_result'].get('server_port'),
|
||||
'int_ping_results': srv['raw_ping_result'].get('int_ping_results'),
|
||||
'online': srv['raw_ping_result'].get('online'),
|
||||
'max': srv['raw_ping_result'].get('max'),
|
||||
'players': srv['raw_ping_result'].get('players'),
|
||||
'desc': srv['raw_ping_result'].get('desc'),
|
||||
'version': srv['raw_ping_result'].get('version'),
|
||||
'icon': srv['raw_ping_result'].get('icon')
|
||||
})
|
||||
if (len(websocket_helper.clients) > 0):
|
||||
websocket_helper.broadcast_page_params(
|
||||
'/panel/server_detail',
|
||||
{
|
||||
'id': str(server_id)
|
||||
},
|
||||
'update_server_details',
|
||||
{
|
||||
'id': srv['raw_ping_result'].get('id'),
|
||||
'started': srv['raw_ping_result'].get('started'),
|
||||
'running': srv['raw_ping_result'].get('running'),
|
||||
'cpu': srv['raw_ping_result'].get('cpu'),
|
||||
'mem': srv['raw_ping_result'].get('mem'),
|
||||
'mem_percent': srv['raw_ping_result'].get('mem_percent'),
|
||||
'world_name': srv['raw_ping_result'].get('world_name'),
|
||||
'world_size': srv['raw_ping_result'].get('world_size'),
|
||||
'server_port': srv['raw_ping_result'].get('server_port'),
|
||||
'int_ping_results': srv['raw_ping_result'].get('int_ping_results'),
|
||||
'online': srv['raw_ping_result'].get('online'),
|
||||
'max': srv['raw_ping_result'].get('max'),
|
||||
'players': srv['raw_ping_result'].get('players'),
|
||||
'desc': srv['raw_ping_result'].get('desc'),
|
||||
'version': srv['raw_ping_result'].get('version'),
|
||||
'icon': srv['raw_ping_result'].get('icon')
|
||||
}
|
||||
)
|
||||
|
||||
if (len(servers_ping) > 0) & (len(websocket_helper.clients) > 0):
|
||||
try:
|
||||
websocket_helper.broadcast_page('/panel/dashboard', 'update_server_status', servers_ping)
|
||||
websocket_helper.broadcast_page('/status', 'update_server_status', servers_ping)
|
||||
except:
|
||||
console.warning("Can't broadcast server status to websocket")
|
||||
time.sleep(5)
|
||||
|
||||
def log_watcher(self):
|
||||
self.controller.servers.check_for_old_logs()
|
||||
|
@ -1,73 +1,76 @@
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
import logging
|
||||
import os
|
||||
import typing as t
|
||||
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.shared.helpers import helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Translation():
|
||||
|
||||
class Translation:
|
||||
def __init__(self):
|
||||
self.translations_path = os.path.join(helper.root_dir, 'app', 'translations')
|
||||
self.cached_translation = None
|
||||
self.cached_translation_lang = None
|
||||
self.lang_file_exists = []
|
||||
|
||||
def translate(self, page, word, lang):
|
||||
translated_word = None
|
||||
fallback_lang = 'en_EN'
|
||||
def get_language_file(self, language: str):
|
||||
return os.path.join(self.translations_path, str(language) + '.json')
|
||||
|
||||
if lang not in self.lang_file_exists and \
|
||||
helper.check_file_exists(os.path.join(self.translations_path, str(lang) + '.json')):
|
||||
self.lang_file_exists.append(lang)
|
||||
def translate(self, page, word, language):
|
||||
fallback_language = 'en_EN'
|
||||
|
||||
translated_word = self.translate_inner(page, word, lang) \
|
||||
if lang in self.lang_file_exists else self.translate_inner(page, word, fallback_lang)
|
||||
translated_word = self.translate_inner(page, word, language)
|
||||
if translated_word is None:
|
||||
translated_word = self.translate_inner(page, word, fallback_language)
|
||||
|
||||
if translated_word:
|
||||
if isinstance(translated_word, dict): return json.dumps(translated_word)
|
||||
elif iter(translated_word) and not isinstance(translated_word, str): return '\n'.join(translated_word)
|
||||
return translated_word
|
||||
if isinstance(translated_word, dict):
|
||||
# JSON objects
|
||||
return json.dumps(translated_word)
|
||||
elif isinstance(translated_word, str):
|
||||
# Basic strings
|
||||
return translated_word
|
||||
elif hasattr(translated_word, '__iter__'):
|
||||
# Multiline strings
|
||||
return '\n'.join(translated_word)
|
||||
return 'Error while getting translation'
|
||||
|
||||
def translate_inner(self, page, word, lang):
|
||||
lang_file = os.path.join(
|
||||
self.translations_path,
|
||||
lang + '.json'
|
||||
)
|
||||
def translate_inner(self, page, word, language) -> t.Union[t.Any, None]:
|
||||
language_file = self.get_language_file(language)
|
||||
try:
|
||||
if not self.cached_translation:
|
||||
with open(lang_file, 'r', encoding='utf-8') as f:
|
||||
with open(language_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
self.cached_translation = data
|
||||
elif self.cached_translation_lang != lang:
|
||||
with open(lang_file, 'r', encoding='utf-8') as f:
|
||||
elif self.cached_translation_lang != language:
|
||||
with open(language_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
self.cached_translation = data
|
||||
self.cached_translation_lang = lang
|
||||
self.cached_translation_lang = language
|
||||
else:
|
||||
data = self.cached_translation
|
||||
|
||||
try:
|
||||
translated_page = data[page]
|
||||
except KeyError:
|
||||
logger.error('Translation File Error: page {} does not exist for lang {}'.format(page, lang))
|
||||
console.error('Translation File Error: page {} does not exist for lang {}'.format(page, lang))
|
||||
logger.error('Translation File Error: page {} does not exist for lang {}'.format(page, language))
|
||||
console.error('Translation File Error: page {} does not exist for lang {}'.format(page, language))
|
||||
return None
|
||||
|
||||
try:
|
||||
translated_word = translated_page[word]
|
||||
return translated_word
|
||||
except KeyError:
|
||||
logger.error('Translation File Error: word {} does not exist on page {} for lang {}'.format(word, page, lang))
|
||||
console.error('Translation File Error: word {} does not exist on page {} for lang {}'.format(word, page, lang))
|
||||
logger.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}')
|
||||
console.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}')
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.critical('Translation File Error: Unable to read {} due to {}'.format(lang_file, e))
|
||||
console.critical('Translation File Error: Unable to read {} due to {}'.format(lang_file, e))
|
||||
logger.critical(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
|
||||
|
||||
translation = Translation()
|
||||
|
||||
translation = Translation()
|
||||
|
@ -35,13 +35,13 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def get(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
_, _, exec_user = self.current_user
|
||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||
|
||||
template = "panel/denied.html"
|
||||
|
||||
page_data = {
|
||||
'user_data': user_data,
|
||||
'user_data': exec_user,
|
||||
'error': error
|
||||
}
|
||||
|
||||
@ -164,10 +164,13 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def post(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -178,17 +181,17 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_user_id, server_id)
|
||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||
|
||||
page_data = {
|
||||
'user_data': user_data,
|
||||
'user_data': exec_user,
|
||||
'error': error
|
||||
}
|
||||
|
||||
if page == "send_command":
|
||||
command = self.get_body_argument('command', default=None, strip=True)
|
||||
server_id = self.get_argument('id')
|
||||
server_id = self.get_argument('id', None)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in send_command ajax call")
|
||||
@ -196,15 +199,23 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
srv_obj = self.controller.get_server_obj(server_id)
|
||||
|
||||
if command == srv_obj.settings['stop_command']:
|
||||
logger.info("Stop command detected as terminal input - intercepting. Starting Crafty's stop process for server with id: {}.".format(server_id))
|
||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'stop_server')
|
||||
command = None
|
||||
elif command == 'restart':
|
||||
logger.info("Restart command detected as terminal input - intercepting. Starting Crafty's stop process for server with id: {}.".format(server_id))
|
||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'restart_server')
|
||||
command = None
|
||||
if command:
|
||||
if srv_obj.check_running():
|
||||
srv_obj.send_command(command)
|
||||
|
||||
self.controller.management.add_to_audit_log(user_data['user_id'], "Sent command to {} terminal: {}".format(self.controller.servers.get_server_friendly_name(server_id), command), server_id, self.get_remote_ip())
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'], "Sent command to {} terminal: {}".format(self.controller.servers.get_server_friendly_name(server_id), command), server_id, self.get_remote_ip())
|
||||
|
||||
elif page == "create_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True))
|
||||
@ -227,7 +238,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "create_dir":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True))
|
||||
@ -248,7 +259,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "unzip_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -259,7 +270,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "kill":
|
||||
if not permissions['Commands'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Commands")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -272,11 +283,11 @@ class AjaxHandler(BaseHandler):
|
||||
elif page == "eula":
|
||||
server_id = self.get_argument('id', None)
|
||||
svr = self.controller.get_server_obj(server_id)
|
||||
svr.agree_eula(user_data['user_id'])
|
||||
svr.agree_eula(exec_user['user_id'])
|
||||
|
||||
elif page == "restore_backup":
|
||||
if not permissions['Backup'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||
return
|
||||
server_id = bleach.clean(self.get_argument('id', None))
|
||||
@ -295,16 +306,21 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "unzip_server":
|
||||
path = self.get_argument('path', None)
|
||||
helper.unzipServer(path, exec_user_id)
|
||||
helper.unzipServer(path, exec_user['user_id'])
|
||||
return
|
||||
|
||||
|
||||
@tornado.web.authenticated
|
||||
def delete(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
|
||||
|
||||
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -315,10 +331,10 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_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 not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
||||
@ -350,7 +366,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
if page == "del_backup":
|
||||
if not permissions['Backup'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||
return
|
||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
||||
@ -376,7 +392,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "del_dir":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
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))
|
||||
@ -401,30 +417,52 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "delete_server":
|
||||
if not permissions['Config'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
logger.info(
|
||||
"Removing server from panel for server: {}".format(self.controller.servers.get_server_friendly_name(server_id)))
|
||||
|
||||
server_data = self.controller.get_server_data(server_id)
|
||||
server_name = server_data['server_name']
|
||||
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"Deleted server {} named {}".format(server_id, server_name),
|
||||
server_id,
|
||||
self.get_remote_ip())
|
||||
|
||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||
self.controller.remove_server(server_id, False)
|
||||
|
||||
elif page == "delete_server_files":
|
||||
if not permissions['Config'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
logger.info(
|
||||
"Removing server and all associated files for server: {}".format(self.controller.servers.get_server_friendly_name(server_id)))
|
||||
|
||||
server_data = self.controller.get_server_data(server_id)
|
||||
server_name = server_data['server_name']
|
||||
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"Deleted server {} named {}".format(server_id, server_name),
|
||||
server_id,
|
||||
self.get_remote_ip())
|
||||
|
||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||
self.controller.remove_server(server_id, True)
|
||||
|
||||
@tornado.web.authenticated
|
||||
def put(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -435,10 +473,10 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_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 not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_contents = self.get_body_argument('file_contents', default=None, strip=True)
|
||||
@ -460,7 +498,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "rename_item":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True))
|
||||
|
@ -1,14 +1,11 @@
|
||||
import os
|
||||
import secrets
|
||||
import threading
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
import logging
|
||||
import re
|
||||
|
||||
from app.classes.web.base_handler import BaseHandler
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
bearer_pattern = re.compile(r'^Bearer', flags=re.IGNORECASE)
|
||||
|
||||
class ApiHandler(BaseHandler):
|
||||
|
||||
@ -16,7 +13,7 @@ class ApiHandler(BaseHandler):
|
||||
# Define a standardized response
|
||||
self.set_status(status)
|
||||
self.write(data)
|
||||
|
||||
|
||||
def access_denied(self, user, reason=''):
|
||||
if reason: reason = ' because ' + reason
|
||||
log.info("User %s from IP %s was denied access to the API route " + self.request.path + reason, user, self.get_remote_ip())
|
||||
@ -28,8 +25,14 @@ class ApiHandler(BaseHandler):
|
||||
def authenticate_user(self) -> bool:
|
||||
try:
|
||||
log.debug("Searching for specified token")
|
||||
# TODO: YEET THIS
|
||||
user_data = self.controller.users.get_user_by_api_token(self.get_argument('token'))
|
||||
|
||||
api_token = self.get_argument('token', '')
|
||||
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')
|
||||
user_data = self.controller.users.get_user_by_api_token(api_token)
|
||||
|
||||
log.debug("Checking results")
|
||||
if user_data:
|
||||
# Login successful! Check perms
|
||||
@ -40,11 +43,11 @@ class ApiHandler(BaseHandler):
|
||||
else:
|
||||
logging.debug("Auth unsuccessful")
|
||||
self.access_denied("unknown", "the user provided an invalid token")
|
||||
return
|
||||
return False
|
||||
except Exception as e:
|
||||
log.warning("An error occured while authenticating an API user: %s", e)
|
||||
self.access_denied("unknown"), "an error occured while authenticating the user"
|
||||
return
|
||||
return False
|
||||
|
||||
|
||||
class ServersStats(ApiHandler):
|
||||
|
@ -4,10 +4,12 @@ import bleach
|
||||
from typing import (
|
||||
Union,
|
||||
List,
|
||||
Optional
|
||||
Optional, Tuple, Dict, Any
|
||||
)
|
||||
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.main_controller import Controller
|
||||
from app.classes.models.users import ApiKeys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -17,7 +19,8 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
nobleach = {bool, type(None)}
|
||||
redactables = ("pass", "api")
|
||||
|
||||
def initialize(self, controller : Controller = None, tasks_manager=None, translator=None):
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def initialize(self, controller: Controller = None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
self.tasks_manager = tasks_manager
|
||||
self.translator = translator
|
||||
@ -28,8 +31,9 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
self.request.remote_ip
|
||||
return remote_ip
|
||||
|
||||
def get_current_user(self):
|
||||
return self.get_secure_cookie("user", max_age_days=1)
|
||||
current_user: Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]
|
||||
def get_current_user(self) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
||||
return authentication.check(self.get_cookie("token"))
|
||||
|
||||
def autobleach(self, name, text):
|
||||
for r in self.redactables:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import requests
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.helpers import Helpers, helper
|
||||
from app.classes.web.base_handler import BaseHandler
|
||||
from app.classes.shared.console import console
|
||||
@ -27,7 +28,7 @@ except ModuleNotFoundError as e:
|
||||
|
||||
class PublicHandler(BaseHandler):
|
||||
|
||||
def set_current_user(self, user):
|
||||
def set_current_user(self, user_id: str = None):
|
||||
|
||||
expire_days = helper.get_setting('cookie_expire')
|
||||
|
||||
@ -35,8 +36,8 @@ class PublicHandler(BaseHandler):
|
||||
if not expire_days:
|
||||
expire_days = "5"
|
||||
|
||||
if user:
|
||||
self.set_secure_cookie("user", tornado.escape.json_encode(user), expires_days=int(expire_days))
|
||||
if user_id is not None:
|
||||
self.set_cookie("token", authentication.generate(user_id), expires_days=int(expire_days))
|
||||
else:
|
||||
self.clear_cookie("user")
|
||||
|
||||
@ -45,12 +46,7 @@ class PublicHandler(BaseHandler):
|
||||
error = bleach.clean(self.get_argument('error', "Invalid Login!"))
|
||||
error_msg = bleach.clean(self.get_argument('error_msg', ''))
|
||||
|
||||
page_data = {
|
||||
'version': helper.get_version_string(),
|
||||
'error': error
|
||||
}
|
||||
|
||||
page_data['lang'] = tornado.locale.get("en_EN")
|
||||
page_data = {'version': helper.get_version_string(), 'error': error, 'lang': helper.get_setting('language')}
|
||||
|
||||
# sensible defaults
|
||||
template = "public/404.html"
|
||||
@ -112,7 +108,7 @@ class PublicHandler(BaseHandler):
|
||||
|
||||
# Valid Login
|
||||
if login_result:
|
||||
self.set_current_user(entered_username)
|
||||
self.set_current_user(user_data.user_id)
|
||||
logger.info("User: {} Logged in from IP: {}".format(user_data, self.get_remote_ip()))
|
||||
|
||||
# record this login
|
||||
@ -124,32 +120,6 @@ class PublicHandler(BaseHandler):
|
||||
# log this login
|
||||
self.controller.management.add_to_audit_log(user_data.user_id, "Logged in", 0, self.get_remote_ip())
|
||||
|
||||
if helper.get_setting("allow_nsfw_profile_pictures"):
|
||||
rating = "x"
|
||||
else:
|
||||
rating = "g"
|
||||
|
||||
|
||||
#Get grvatar hash for profile pictures
|
||||
if user_data.email != 'default@example.com' or "":
|
||||
g = libgravatar.Gravatar(libgravatar.sanitize_email(user_data.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:
|
||||
profile_url = url
|
||||
else:
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
else:
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
cookie_data = {
|
||||
"username": user_data.username,
|
||||
"user_id": user_data.user_id,
|
||||
"email": user_data.email,
|
||||
"profile_url": profile_url,
|
||||
"account_type": user_data.superuser,
|
||||
}
|
||||
|
||||
self.set_secure_cookie('user_data', json.dumps(cookie_data))
|
||||
|
||||
next_page = "/panel/dashboard"
|
||||
self.redirect(next_page)
|
||||
else:
|
||||
|
@ -9,6 +9,8 @@ from app.classes.web.base_handler import BaseHandler
|
||||
from app.classes.models.crafty_permissions import Enum_Permissions_Crafty
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.shared.helpers import helper
|
||||
import libgravatar
|
||||
import requests
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -28,13 +30,13 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def get(self, page):
|
||||
# name = tornado.escape.json_decode(self.current_user)
|
||||
exec_user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user_id = exec_user_data['user_id']
|
||||
exec_user = self.controller.users.get_user_by_id(exec_user_id)
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
exec_user_role = set()
|
||||
if exec_user['superuser'] == 1:
|
||||
if superuser:
|
||||
defined_servers = self.controller.list_defined_servers()
|
||||
exec_user_role.add("Super User")
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
@ -42,8 +44,8 @@ class ServerHandler(BaseHandler):
|
||||
for role in self.controller.roles.get_all_roles():
|
||||
list_roles.append(self.controller.roles.get_role(role.role_id))
|
||||
else:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user_id)
|
||||
defined_servers = self.controller.servers.get_authorized_servers(exec_user_id)
|
||||
exec_user_crafty_permissions = 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 = []
|
||||
for r in exec_user['roles']:
|
||||
role = self.controller.roles.get_role(r)
|
||||
@ -54,7 +56,7 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
page_data = {
|
||||
'version_data': helper.get_version_string(),
|
||||
'user_data': exec_user_data,
|
||||
'user_data': exec_user,
|
||||
'user_role' : exec_user_role,
|
||||
'roles' : list_roles,
|
||||
'user_crafty_permissions' : exec_user_crafty_permissions,
|
||||
@ -71,13 +73,38 @@ class ServerHandler(BaseHandler):
|
||||
'hosts_data': self.controller.management.get_latest_hosts_stats(),
|
||||
'menu_servers': defined_servers,
|
||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user_id)
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||
'api_key': {
|
||||
'name': api_key.name,
|
||||
'created': api_key.created,
|
||||
'server_permissions': api_key.server_permissions,
|
||||
'crafty_permissions': api_key.crafty_permissions,
|
||||
'superuser': api_key.superuser
|
||||
} if api_key is not None else None,
|
||||
'superuser': superuser
|
||||
}
|
||||
if exec_user['superuser'] == 1:
|
||||
if helper.get_setting("allow_nsfw_profile_pictures"):
|
||||
rating = "x"
|
||||
else:
|
||||
rating = "g"
|
||||
|
||||
|
||||
if exec_user['email'] != 'default@example.com' or "":
|
||||
g = libgravatar.Gravatar(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:
|
||||
profile_url = url
|
||||
else:
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
else:
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
|
||||
page_data['user_image'] = profile_url
|
||||
if superuser:
|
||||
page_data['roles'] = list_roles
|
||||
|
||||
if page == "step1":
|
||||
if not exec_user['superuser'] and not self.controller.crafty_perms.can_create_server(exec_user_id):
|
||||
if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]):
|
||||
self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached")
|
||||
return
|
||||
|
||||
@ -93,17 +120,17 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def post(self, page):
|
||||
|
||||
exec_user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user_id = exec_user_data['user_id']
|
||||
exec_user = self.controller.users.get_user_by_id(exec_user_id)
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
template = "public/404.html"
|
||||
page_data = {
|
||||
'version_data': "version_data_here",
|
||||
'user_data': exec_user_data,
|
||||
'version_data': "version_data_here", # TODO
|
||||
'user_data': exec_user,
|
||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user_id)
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
}
|
||||
|
||||
if page == "command":
|
||||
@ -151,11 +178,11 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
return
|
||||
|
||||
self.controller.management.send_command(exec_user_data['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 not exec_user['superuser']:
|
||||
if not superuser:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
else:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
@ -185,7 +212,7 @@ class ServerHandler(BaseHandler):
|
||||
return
|
||||
|
||||
new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"imported a jar server named \"{}\"".format(server_name), # Example: Admin imported a server named "old creative"
|
||||
new_server_id,
|
||||
self.get_remote_ip())
|
||||
@ -201,7 +228,7 @@ class ServerHandler(BaseHandler):
|
||||
if new_server_id == "false":
|
||||
self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with sudo chown -R crafty:crafty {} And sudo chmod 2775 -R {}".format(import_server_path, import_server_path))
|
||||
return
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"imported a zip server named \"{}\"".format(server_name), # Example: Admin imported a server named "old creative"
|
||||
new_server_id,
|
||||
self.get_remote_ip())
|
||||
@ -213,21 +240,21 @@ class ServerHandler(BaseHandler):
|
||||
return
|
||||
server_type, server_version = server_parts
|
||||
# TODO: add server type check here and call the correct server add functions if not a jar
|
||||
role_ids = self.controller.users.get_user_roles_id(exec_user_id)
|
||||
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
|
||||
new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port)
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"created a {} {} server named \"{}\"".format(server_version, str(server_type).capitalize(), 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
|
||||
if len(captured_roles) == 0:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid")
|
||||
role_id = self.controller.roles.add_role("Creator of Server with uuid={}".format(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_id, role_id)
|
||||
self.controller.crafty_perms.add_server_creation(exec_user_id)
|
||||
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:
|
||||
for role in captured_roles:
|
||||
|
@ -25,7 +25,7 @@ except ModuleNotFoundError as e:
|
||||
class StatusHandler(BaseHandler):
|
||||
def get(self):
|
||||
page_data = {}
|
||||
page_data['lang'] = tornado.locale.get("en_EN")
|
||||
page_data['lang'] = helper.get_setting('language')
|
||||
page_data['servers'] = self.controller.servers.get_all_servers_stats()
|
||||
for srv in page_data['servers']:
|
||||
server_data = srv.get('server_data', False)
|
||||
|
@ -116,6 +116,7 @@ class Webserver:
|
||||
|
||||
tornado.template.Loader('.')
|
||||
|
||||
# TODO: Remove because we don't and won't use
|
||||
tornado.locale.set_default_locale('en_EN')
|
||||
|
||||
handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager, "translator": translation}
|
||||
|
@ -20,6 +20,7 @@ MAX_STREAMED_SIZE = 1024 * 1024 * 1024
|
||||
@tornado.web.stream_request_body
|
||||
class UploadHandler(tornado.web.RequestHandler):
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def initialize(self, controller: Controller=None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
self.tasks_manager = tasks_manager
|
||||
@ -27,8 +28,19 @@ class UploadHandler(tornado.web.RequestHandler):
|
||||
|
||||
def prepare(self):
|
||||
self.do_upload = True
|
||||
user_data = json.loads(self.get_secure_cookie('user_data'))
|
||||
user_id = user_data['user_id']
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
user_id = exec_user['user_id']
|
||||
|
||||
if superuser:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
elif 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(
|
||||
exec_user["user_id"])
|
||||
|
||||
server_id = self.request.headers.get('X-ServerId', None)
|
||||
|
||||
@ -42,8 +54,7 @@ class UploadHandler(tornado.web.RequestHandler):
|
||||
console.warning('Server ID not found in upload handler call')
|
||||
self.do_upload = False
|
||||
|
||||
user_permissions = self.controller.server_perms.get_user_permissions_list(user_id, server_id)
|
||||
if Enum_Permissions_Server.Files not in user_permissions:
|
||||
if Enum_Permissions_Server.Files not in exec_user_crafty_permissions:
|
||||
logger.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
||||
console.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
||||
self.do_upload = False
|
||||
|
@ -5,6 +5,7 @@ import sys
|
||||
|
||||
from urllib.parse import parse_qsl
|
||||
from app.classes.models.users import Users
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.shared.console import console
|
||||
@ -19,7 +20,14 @@ except ModuleNotFoundError as e:
|
||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
page = None
|
||||
page_query_params = None
|
||||
controller = None
|
||||
tasks_manager = None
|
||||
translator = None
|
||||
io_loop = None
|
||||
|
||||
def initialize(self, controller=None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
@ -34,24 +42,11 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
return remote_ip
|
||||
|
||||
def get_user_id(self):
|
||||
user_data_cookie_raw = self.get_secure_cookie('user_data')
|
||||
|
||||
if user_data_cookie_raw and user_data_cookie_raw.decode('utf-8'):
|
||||
user_data_cookie = user_data_cookie_raw.decode('utf-8')
|
||||
user_id = json.loads(user_data_cookie)['user_id']
|
||||
return user_id
|
||||
_, _, user = authentication.check(self.get_cookie('token'))
|
||||
return user['user_id']
|
||||
|
||||
def check_auth(self):
|
||||
user_data_cookie_raw = self.get_secure_cookie('user_data')
|
||||
|
||||
if user_data_cookie_raw and user_data_cookie_raw.decode('utf-8'):
|
||||
user_data_cookie = user_data_cookie_raw.decode('utf-8')
|
||||
user_id = json.loads(user_data_cookie)['user_id']
|
||||
query = Users.select().where(Users.user_id == user_id)
|
||||
if query.exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
return authentication.check_bool(self.get_cookie('token'))
|
||||
|
||||
def open(self):
|
||||
logger.debug('Checking WebSocket authentication')
|
||||
@ -74,10 +69,11 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
logger.debug('Opened WebSocket connection')
|
||||
# websocket_helper.broadcast('notification', 'New client connected')
|
||||
|
||||
def on_message(self, rawMessage):
|
||||
@staticmethod
|
||||
def on_message(raw_message):
|
||||
|
||||
logger.debug('Got message from WebSocket connection {}'.format(rawMessage))
|
||||
message = json.loads(rawMessage)
|
||||
logger.debug('Got message from WebSocket connection {}'.format(raw_message))
|
||||
message = json.loads(raw_message)
|
||||
logger.debug('Event Type: {}, Data: {}'.format(message['event'], message['data']))
|
||||
|
||||
def on_close(self):
|
||||
|
@ -5,6 +5,7 @@
|
||||
"language": "en_EN",
|
||||
"cookie_expire": 30,
|
||||
"cookie_secret": "random",
|
||||
"apikey_secret": "random",
|
||||
"show_errors": true,
|
||||
"history_max_age": 7,
|
||||
"stats_update_frequency": 30,
|
||||
|
@ -50,7 +50,11 @@
|
||||
}
|
||||
|
||||
.mc-log-error{
|
||||
color:#ff6258;
|
||||
color:#af463f;
|
||||
}
|
||||
|
||||
.mc-log-fatal{
|
||||
color:#da0f00;
|
||||
}
|
||||
|
||||
.mc-log-keyword{
|
||||
|
@ -1,153 +1,162 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
{% block meta %}{% end %}
|
||||
<title>{% block title %}{{ _('Default') }}{% end %}</title>
|
||||
|
||||
<!-- plugins:css -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.css"/>
|
||||
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
||||
<link rel="stylesheet" href="/static/assets/css/crafty.css">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
{% block meta %}{% end %}
|
||||
<title>{% block title %}{{ _('Default') }}{% end %}</title>
|
||||
|
||||
<!-- endinject -->
|
||||
<!-- plugins:css -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.css" />
|
||||
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
||||
<link rel="stylesheet" href="/static/assets/css/crafty.css">
|
||||
|
||||
<!-- Plugin css for this page -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/jvectormap/jquery-jvectormap.css">
|
||||
<!-- End Plugin css for this page -->
|
||||
<!-- endinject -->
|
||||
|
||||
<!-- Layout styles -->
|
||||
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
||||
<!-- End Layout styles -->
|
||||
<!-- Plugin css for this page -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/jvectormap/jquery-jvectormap.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<!-- End Plugin css for this page -->
|
||||
|
||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
||||
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
||||
<link rel="stylesheet" href="/static/assets/css/crafty.css">
|
||||
<!-- Layout styles -->
|
||||
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
||||
<!-- End Layout styles -->
|
||||
|
||||
</head>
|
||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
||||
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
||||
<link rel="stylesheet" href="/static/assets/css/crafty.css">
|
||||
|
||||
<body class="dark-theme">
|
||||
<div class="container-scroller">
|
||||
<!-- partial:partials/_navbar.html -->
|
||||
<nav class="navbar default-layout col-lg-12 col-12 p-0 fixed-top d-flex flex-row">
|
||||
<div class="text-center navbar-brand-wrapper d-flex align-items-top justify-content-center">
|
||||
<a class="navbar-brand brand-logo" href="/panel/dashboard">
|
||||
<img src="/static/assets/images/logo_long.svg" alt="logo" /> </a>
|
||||
<a class="navbar-brand brand-logo-mini" href="/panel/dashboard">
|
||||
<img src="/static/assets/images/logo_small.svg" alt="logo" /> </a>
|
||||
</div>
|
||||
<div class="navbar-menu-wrapper d-flex align-items-center">
|
||||
<button class="navbar-toggler navbar-toggler align-self-center" type="button" data-toggle="minimize">
|
||||
<span class="mdi mdi-menu"></span>
|
||||
</button>
|
||||
</head>
|
||||
|
||||
<body class="dark-theme">
|
||||
<div class="container-scroller">
|
||||
<!-- partial:partials/_navbar.html -->
|
||||
<nav class="navbar default-layout col-lg-12 col-12 p-0 fixed-top d-flex flex-row">
|
||||
<div class="text-center navbar-brand-wrapper d-flex align-items-top justify-content-center">
|
||||
<a class="navbar-brand brand-logo" href="/panel/dashboard">
|
||||
<img src="/static/assets/images/logo_long.svg" alt="logo" /> </a>
|
||||
<a class="navbar-brand brand-logo-mini" href="/panel/dashboard">
|
||||
<img src="/static/assets/images/logo_small.svg" alt="logo" /> </a>
|
||||
</div>
|
||||
<div class="navbar-menu-wrapper d-flex align-items-center">
|
||||
<button class="navbar-toggler navbar-toggler align-self-center" type="button" data-toggle="minimize">
|
||||
<span class="mdi mdi-menu"></span>
|
||||
</button>
|
||||
|
||||
{% include notify.html %}
|
||||
|
||||
<button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button" data-toggle="offcanvas">
|
||||
<span class="mdi mdi-menu"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% include main_menu.html %}
|
||||
|
||||
<div class="main-panel">
|
||||
|
||||
<div class="warnings">
|
||||
<noscript class="noscript-warning" style="padding: 20px; background-color: rgb(247, 151, 15);">
|
||||
<div>{% raw translate('base', 'doesNotWorkWithoutJavascript', data['lang']) %}</div>
|
||||
</noscript>
|
||||
</div>
|
||||
|
||||
{% block content %}
|
||||
{% end %}
|
||||
|
||||
{% include footer.html %}
|
||||
|
||||
</div>
|
||||
<!-- main-panel ends -->
|
||||
|
||||
<button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button"
|
||||
data-toggle="offcanvas">
|
||||
<span class="mdi mdi-menu"></span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- page-body-wrapper ends -->
|
||||
</nav>
|
||||
|
||||
{% include main_menu.html %}
|
||||
|
||||
<div class="main-panel">
|
||||
|
||||
<div class="warnings">
|
||||
<noscript class="noscript-warning" style="padding: 20px; background-color: rgb(247, 151, 15);">
|
||||
<div>{% raw translate('base', 'doesNotWorkWithoutJavascript', data['lang']) %}</div>
|
||||
</noscript>
|
||||
</div>
|
||||
|
||||
{% block content %}
|
||||
{% end %}
|
||||
|
||||
{% include footer.html %}
|
||||
|
||||
</div>
|
||||
<!-- main-panel ends -->
|
||||
|
||||
</div>
|
||||
<!-- page-body-wrapper ends -->
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.notifications {
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
top: 70px;
|
||||
right: 0px;
|
||||
}
|
||||
.notification {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem;
|
||||
padding-left: 0.7rem;
|
||||
width: 180px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
background: #282a40;
|
||||
transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
right: -6rem;
|
||||
opacity: 0.1;
|
||||
margin-bottom: 1rem;
|
||||
z-index: 999;
|
||||
top: 0px;
|
||||
}
|
||||
.notification.active {
|
||||
right: 0rem;
|
||||
opacity: 1;
|
||||
}
|
||||
.notification.remove {
|
||||
right: 0rem;
|
||||
opacity: 0.1;
|
||||
top: -2rem;
|
||||
}
|
||||
.notification p {
|
||||
margin: 0px;
|
||||
width: calc(160.8px - 16px);
|
||||
z-index: inherit;
|
||||
}
|
||||
.notification span {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 0.46rem;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
font-size: 22px;
|
||||
user-select: none;
|
||||
z-index: inherit;
|
||||
}
|
||||
</style>
|
||||
<div class="notifications"></div>
|
||||
<style>
|
||||
.notifications {
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
top: 70px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem;
|
||||
padding-left: 0.7rem;
|
||||
width: 180px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
background: #282a40;
|
||||
transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
right: -6rem;
|
||||
opacity: 0.1;
|
||||
margin-bottom: 1rem;
|
||||
z-index: 999;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.notification.active {
|
||||
right: 0rem;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.notification.remove {
|
||||
right: 0rem;
|
||||
opacity: 0.1;
|
||||
top: -2rem;
|
||||
}
|
||||
|
||||
.notification p {
|
||||
margin: 0px;
|
||||
width: calc(160.8px - 16px);
|
||||
z-index: inherit;
|
||||
}
|
||||
|
||||
.notification span {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 0.46rem;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
font-size: 22px;
|
||||
user-select: none;
|
||||
z-index: inherit;
|
||||
}
|
||||
</style>
|
||||
<div class="notifications"></div>
|
||||
|
||||
|
||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
<script src="/static/assets/js/shared/misc.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
|
||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
<script src="/static/assets/js/shared/misc.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
|
||||
<script type="text/javascript" src="/static/assets/js/motd.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
$.extend($.fn.dataTable.defaults, {
|
||||
language: {% raw translate('datatables', 'i18n', data['lang']) %}
|
||||
<script>
|
||||
$.extend($.fn.dataTable.defaults, {
|
||||
language: {% raw translate('datatables', 'i18n', data['lang']) %}
|
||||
})
|
||||
|
||||
//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 getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
// tool tips
|
||||
@ -156,13 +165,13 @@
|
||||
})
|
||||
|
||||
// Notify
|
||||
$(document).ready(function(){
|
||||
$("#notificationDropdown").click(function(){
|
||||
$.get("/ajax/announcements", function(data){
|
||||
$(document).ready(function () {
|
||||
$("#notificationDropdown").click(function () {
|
||||
$.get("/ajax/announcements", function (data) {
|
||||
console.log(data);
|
||||
bootbox.alert({
|
||||
title: "Notifications",
|
||||
message: data,
|
||||
title: "Notifications",
|
||||
message: data,
|
||||
|
||||
});
|
||||
});
|
||||
@ -170,146 +179,150 @@
|
||||
});
|
||||
|
||||
// {% if request.protocol == 'https' %}
|
||||
let usingWebSockets = true;
|
||||
let usingWebSockets = true;
|
||||
|
||||
let listenEvents = [];
|
||||
let listenEvents = [];
|
||||
|
||||
try {
|
||||
pageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
|
||||
page = 'page=' + encodeURIComponent(location.pathname)
|
||||
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + page + '&' + pageQueryParams);
|
||||
wsInternal.onopen = function() {
|
||||
console.log('opened WebSocket connection:', wsInternal)
|
||||
};
|
||||
wsInternal.onmessage = function (rawMessage) {
|
||||
var message = JSON.parse(rawMessage.data);
|
||||
try {
|
||||
pageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
|
||||
page = 'page=' + encodeURIComponent(location.pathname)
|
||||
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + page + '&' + pageQueryParams);
|
||||
wsInternal.onopen = function () {
|
||||
console.log('opened WebSocket connection:', wsInternal)
|
||||
};
|
||||
wsInternal.onmessage = function (rawMessage) {
|
||||
var message = JSON.parse(rawMessage.data);
|
||||
|
||||
console.log('got message: ', message)
|
||||
console.log('got message: ', message)
|
||||
|
||||
listenEvents
|
||||
.filter(listenedEvent => listenedEvent.event == message.event)
|
||||
.forEach(listenedEvent => listenedEvent.callback(message.data))
|
||||
};
|
||||
wsInternal.onerror = function (errorEvent) {
|
||||
console.error('WebSocket Error', errorEvent);
|
||||
};
|
||||
wsInternal.onclose = function (closeEvent) {
|
||||
console.log('Closed WebSocket', closeEvent);
|
||||
};
|
||||
listenEvents
|
||||
.filter(listenedEvent => listenedEvent.event == message.event)
|
||||
.forEach(listenedEvent => listenedEvent.callback(message.data))
|
||||
};
|
||||
wsInternal.onerror = function (errorEvent) {
|
||||
console.error('WebSocket Error', errorEvent);
|
||||
};
|
||||
wsInternal.onclose = function (closeEvent) {
|
||||
console.log('Closed WebSocket', closeEvent);
|
||||
};
|
||||
|
||||
|
||||
webSocket = {
|
||||
on: function (event, callback) {
|
||||
console.log('registered ' + event + ' event');
|
||||
listenEvents.push({ event: event, callback: callback })
|
||||
},
|
||||
emit: function (event, data) {
|
||||
var message = {
|
||||
event: event,
|
||||
data: data
|
||||
}
|
||||
|
||||
wsInternal.send(JSON.stringify(message));
|
||||
webSocket = {
|
||||
on: function (event, callback) {
|
||||
console.log('registered ' + event + ' event');
|
||||
listenEvents.push({ event: event, callback: callback })
|
||||
},
|
||||
emit: function (event, data) {
|
||||
var message = {
|
||||
event: event,
|
||||
data: data
|
||||
}
|
||||
|
||||
wsInternal.send(JSON.stringify(message));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while making websocket helpers', error);
|
||||
usingWebSockets = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while making websocket helpers', error);
|
||||
usingWebSockets = false;
|
||||
}
|
||||
// {% else %}
|
||||
let usingWebSockets = false;
|
||||
warn('WebSockets are not supported in Crafty if not using the https protocol')
|
||||
var webSocket;
|
||||
let usingWebSockets = false;
|
||||
warn('WebSockets are not supported in Crafty if not using the https protocol')
|
||||
var webSocket;
|
||||
// {% end%}
|
||||
|
||||
if (webSocket) {
|
||||
webSocket.on('send_start_error', function (start_error) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if(x){
|
||||
x.remove()}
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if(x){
|
||||
x.remove()}
|
||||
bootbox.alert({
|
||||
message: start_error.error,
|
||||
callback: function () {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
if (webSocket) {
|
||||
webSocket.on('send_logs_bootbox', function (server_id) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if(x){
|
||||
x.remove()}
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if(x){
|
||||
x.remove()}
|
||||
bootbox.alert({
|
||||
title: 'Download Support Logs?',
|
||||
message: "We've finished preparing your support logs. Please click download to download",
|
||||
buttons: {
|
||||
ok: {
|
||||
label: 'Download',
|
||||
className: 'btn-info'
|
||||
if (webSocket) {
|
||||
webSocket.on('send_start_error', function (start_error) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
},
|
||||
callback: function(){
|
||||
console.log("in callback")
|
||||
location.href="/panel/download_support_package";
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (webSocket) {
|
||||
webSocket.on('send_eula_bootbox', function (server_id) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if(x){
|
||||
x.remove()}
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if(x){
|
||||
x.remove()}
|
||||
bootbox.confirm({
|
||||
title: '{% raw translate("error", "eulaTitle", data['lang']) %}',
|
||||
message: '{% raw translate("error", "eulaMsg", data['lang']) %} <br><br><a href="https://account.mojang.com/documents/minecraft_eula" target="_blank">EULA</a><br><br>{% raw translate("error", "eulaAgree", data['lang']) %}',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes',
|
||||
className: 'btn-info'
|
||||
},
|
||||
cancel: {
|
||||
label: 'No',
|
||||
className: 'btn-secondary'
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
if(result == true){
|
||||
eulaAgree(server_id.id)
|
||||
}
|
||||
else {
|
||||
location.reload()
|
||||
}
|
||||
|
||||
bootbox.alert({
|
||||
message: start_error.error,
|
||||
callback: function () {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
if (webSocket) {
|
||||
webSocket.on('send_logs_bootbox', function (server_id) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
bootbox.alert({
|
||||
title: "{{ translate('notify', 'downloadLogs', data['lang']) }}",
|
||||
message: "{{ translate('notify', 'finishedPreparing', data['lang']) }}",
|
||||
buttons: {
|
||||
ok: {
|
||||
label: 'Download',
|
||||
className: 'btn-info'
|
||||
}
|
||||
},
|
||||
callback: function () {
|
||||
console.log("in callback")
|
||||
location.href = "/panel/download_support_package";
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function eulaAgree (server_id, command){
|
||||
<!-- this getCookie function is in base.html-->
|
||||
if (webSocket) {
|
||||
webSocket.on('send_eula_bootbox', function (server_id) {
|
||||
var x = document.querySelector('.bootbox');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
var x = document.querySelector('.modal-backdrop');
|
||||
if (x) {
|
||||
x.remove()
|
||||
}
|
||||
bootbox.confirm({
|
||||
title: '{% raw translate("error", "eulaTitle", data['lang']) %}',
|
||||
message: '{% raw translate("error", "eulaMsg", data['lang']) %} <br><br><a href="https://account.mojang.com/documents/minecraft_eula" target="_blank">EULA</a><br><br>{% raw translate("error", "eulaAgree", data['lang']) %}',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes',
|
||||
className: 'btn-info'
|
||||
},
|
||||
cancel: {
|
||||
label: 'No',
|
||||
className: 'btn-secondary'
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
if (result == true) {
|
||||
eulaAgree(server_id.id)
|
||||
}
|
||||
else {
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function eulaAgree(server_id, command) {
|
||||
//< !--this getCookie function is in base.html-- >
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/eula?id='+ server_id,
|
||||
success: function(data){
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/ajax/eula?id=' + server_id,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
location.reload();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -332,7 +345,7 @@ if (webSocket) {
|
||||
closeEl.style.lineHeight = '20px';
|
||||
closeEl.style.cursor = 'pointer';
|
||||
|
||||
closeEl.addEventListener('click', function () {this.parentElement.style.display='none';});
|
||||
closeEl.addEventListener('click', function () { this.parentElement.style.display = 'none'; });
|
||||
|
||||
var parentEl = document.createElement('div');
|
||||
|
||||
@ -360,7 +373,7 @@ if (webSocket) {
|
||||
paragraphEl.textContent = message;
|
||||
|
||||
closeEl.innerHTML = '×';
|
||||
closeEl.addEventListener('click', function () {closeNotification(this)});
|
||||
closeEl.addEventListener('click', function () { closeNotification(this) });
|
||||
|
||||
var parentEl = document.createElement('div');
|
||||
parentEl.appendChild(paragraphEl);
|
||||
@ -388,12 +401,26 @@ if (webSocket) {
|
||||
}
|
||||
webSocket.on('notification', notify);
|
||||
|
||||
</script>
|
||||
$(document).ready(function () {
|
||||
$('#support_logs').click(function () {
|
||||
var dialog = bootbox.dialog({
|
||||
message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i>{{ translate('notify', 'preparingLogs', data['lang']) }}</p>',
|
||||
closeButton: false
|
||||
});
|
||||
setTimeout(function () {
|
||||
location.href = "/panel/support_logs";
|
||||
}, 6000);
|
||||
|
||||
{% block js %}
|
||||
<!-- Custom js for this page -->
|
||||
<!-- End custom js for this page -->
|
||||
{% end %}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% block js %}
|
||||
<!-- Custom js for base.html page partial pages -->
|
||||
<!-- End custom js for base.html page -->
|
||||
{% end %}
|
||||
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Blank Page{% end %}
|
||||
|
@ -18,22 +18,25 @@
|
||||
|
||||
<li class="nav-item dropdown user-dropdown">
|
||||
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
||||
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" alt="Profile image"> </a>
|
||||
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_image'] }}" alt="Profile image"> </a>
|
||||
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
||||
<div class="dropdown-header text-center">
|
||||
<img class="img-md rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" alt="Profile image">
|
||||
<img class="img-md rounded-circle profile-picture" src="{{ data['user_image'] }}" alt="Profile image">
|
||||
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
||||
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
||||
{% for r in data['user_role'] %}
|
||||
<p class="font-weight-light text-muted mb-0">{{ r }}</p>
|
||||
{% end %}
|
||||
{% if data.get('api_key') %}
|
||||
<p class="mt-3">Logged in as API key "{{ data['api_key']['name'] }}"</p>
|
||||
{% end %}
|
||||
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
||||
</div>
|
||||
<a class="dropdown-item" href="/panel/support_logs"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i> Support Logs</i></a>
|
||||
{% if "Super User" in data['user_role'] %}
|
||||
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i> Activity</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>
|
||||
{% 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>
|
||||
{% end %}
|
||||
<a class="dropdown-item" href="/public/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>Sign Out</a>
|
||||
<a class="dropdown-item" href="/public/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{ translate('notify', 'logout', data['lang']) }}</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
@ -1,8 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Activity Logs{% end %}
|
||||
@ -93,7 +91,7 @@ $( document ).ready(function() {
|
||||
if($(window).width() < 1000){
|
||||
$('.too_small').popover("show");
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
$(window).ready(function(){
|
||||
$('body').click(function(){
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Contribute{% end %}
|
||||
@ -73,7 +72,7 @@
|
||||
|
||||
<div class="media-body">
|
||||
<p class="card-text">
|
||||
Thank you for your interest in contributing to Aracdia Technology's Crafty Controller.
|
||||
Thank you for your interest in contributing to Aracdia Technology's Crafty Controller.
|
||||
We are always thinking of new ways for our community to contribute to this awesome project. <br><br> If you don't see
|
||||
a contribution method that peaks your interest now please check back soon.
|
||||
</p>
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Credits{% end %}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<meta http-equiv="refresh" content="60">
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('dashboard', 'dashboard', data['lang']) }}{% end %}
|
||||
@ -10,7 +9,7 @@
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
@ -29,16 +28,21 @@
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="d-flex">
|
||||
<div class="wrapper">
|
||||
<h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host', data['lang']) }}</h5>
|
||||
<h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host', data['lang']) }}
|
||||
</h5>
|
||||
<h3 class="mb-0 font-weight-semibold"> <i class="fas fa-chart-line"></i></h3>
|
||||
|
||||
</div>
|
||||
<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" 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">{{ data.get('hosts_data').get('cpu_usage') }}</span>
|
||||
<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') }}">
|
||||
{{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{
|
||||
data.get('hosts_data').get('cpu_usage') }}</span>
|
||||
</p>
|
||||
<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') }}" >
|
||||
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{ data.get('hosts_data').get('mem_percent') }}%</span>
|
||||
<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') }}">
|
||||
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{
|
||||
data.get('hosts_data').get('mem_percent') }}%</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -46,20 +50,24 @@
|
||||
<div class="col-lg-4 col-md-6 mt-md-0 mt-4">
|
||||
<div class="d-flex">
|
||||
<div class="wrapper">
|
||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers', data['lang']) }}</h5>
|
||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers', data['lang']) }}
|
||||
</h5>
|
||||
<h3 class="mb-0 font-weight-semibold">{{ data['server_stats']['total'] }}</h3>
|
||||
|
||||
</div>
|
||||
<div class="wrapper my-auto ml-auto ml-lg-4">
|
||||
<p class="mb-0 text-success">{{ data['server_stats']['running'] }} {{ translate('dashboard', 'online', data['lang']).lower() }}</p>
|
||||
<p class="mb-0 text-warning"> {{ data['server_stats']['stopped'] }} {{ translate('dashboard', 'offline', data['lang']).lower() }}</p>
|
||||
<p class="mb-0 text-success">{{ data['server_stats']['running'] }} {{ translate('dashboard', 'online',
|
||||
data['lang']).lower() }}</p>
|
||||
<p class="mb-0 text-warning"> {{ data['server_stats']['stopped'] }} {{ translate('dashboard',
|
||||
'offline', data['lang']).lower() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 mt-md-0 mt-4">
|
||||
<div class="d-flex">
|
||||
<div class="wrapper">
|
||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players', data['lang']) }}</h5>
|
||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players', data['lang']) }}
|
||||
</h5>
|
||||
<h3 class="mb-0 font-weight-semibold">{{ data['num_players'] }}</h3>
|
||||
|
||||
</div>
|
||||
@ -79,24 +87,29 @@
|
||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||
<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('dashboard', 'allServers', data['lang']) }}</h4>
|
||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('dashboard', 'allServers',
|
||||
data['lang']) }}</h4>
|
||||
{% if len(data['servers']) > 0 %}
|
||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
||||
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||
data-placement="top"></span>
|
||||
{% end %}
|
||||
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{ translate('dashboard', 'newServer', data['lang']) }}</a></div>
|
||||
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{
|
||||
translate('dashboard', 'newServer', data['lang']) }}</a></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if len(data['servers']) == 0%}
|
||||
<div style="text-align: center; color: grey;">
|
||||
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
||||
<br>
|
||||
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer', data['lang']) }}.</h7>
|
||||
</div>
|
||||
|
||||
{% end %}
|
||||
{% if len(data['servers']) > 0 %}
|
||||
{% if len(data['servers']) == 0%}
|
||||
<div style="text-align: center; color: grey;">
|
||||
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
||||
<br>
|
||||
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer',
|
||||
data['lang']) }}.</h7>
|
||||
</div>
|
||||
|
||||
{% end %}
|
||||
{% if len(data['servers']) > 0 %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
@ -121,24 +134,37 @@
|
||||
|
||||
<td id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
||||
{% if server['user_command_permission'] %}
|
||||
{% if server['stats']['running'] %}
|
||||
<a class="stop_button" data-id="{{server['server_data']['server_id']}}" data-toggle="tooltip" title={{ translate('dashboard', 'stop', data['lang']) }}> <i class="fas fa-stop"></i></a>
|
||||
<a class="restart_button" data-id="{{server['server_data']['server_id']}}" data-toggle="tooltip" title={{ translate('dashboard', 'restart', data['lang']) }}> <i class="fas fa-sync"></i></a>
|
||||
<a class="kill_button" 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']%}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating', data['lang']) }}</i></a>
|
||||
{% elif server['stats']['waiting_start']%}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="" title={{ translate('dashboard', 'delay-explained', data['lang'])}}>{{ translate('dashboard', 'starting', data['lang']) }}</i></a>
|
||||
{% else %}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button"><i class="fas fa-play" data-toggle="tooltip" title={{ translate('dashboard', 'start', data['lang']) }}></i></a>
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"> <i class="fas fa-clone" data-toggle="tooltip" title={{ translate('dashboard', 'clone', data['lang']) }}></i></a>
|
||||
<a class="kill_button" 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 %}
|
||||
{% if server['stats']['running'] %}
|
||||
<a class="stop_button" data-id="{{server['server_data']['server_id']}}" data-toggle="tooltip"
|
||||
title={{ translate('dashboard', 'stop' , data['lang']) }}> <i class="fas fa-stop"></i></a>
|
||||
<a class="restart_button" data-id="{{server['server_data']['server_id']}}" data-toggle="tooltip"
|
||||
title={{ translate('dashboard', 'restart' , data['lang']) }}> <i class="fas fa-sync"></i></a>
|
||||
|
||||
<a class="kill_button" 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']%}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating',
|
||||
data['lang']) }}</i></a>
|
||||
{% elif server['stats']['waiting_start']%}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="" title={{
|
||||
translate('dashboard', 'delay-explained' , data['lang'])}}>{{ translate('dashboard', 'starting',
|
||||
data['lang']) }}</i></a>
|
||||
{% else %}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button"><i class="fas fa-play"
|
||||
data-toggle="tooltip" title={{ translate('dashboard', 'start' , data['lang']) }}></i></a>
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"> <i class="fas fa-clone"
|
||||
data-toggle="tooltip" title={{ translate('dashboard', 'clone' , data['lang']) }}></i></a>
|
||||
<a class="kill_button" 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>
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['cpu']}}">
|
||||
<td id="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
|
||||
@ -147,13 +173,15 @@
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
{{server['stats']['cpu']}}%
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['mem']}}">
|
||||
<td id="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
|
||||
@ -162,38 +190,42 @@
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
" 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
|
||||
0 MB
|
||||
{% else %}
|
||||
{{server['stats']['mem']}}
|
||||
{{server['stats']['mem']}}
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
<td id="server_world_{{server['server_data']['server_id']}}">
|
||||
{{ server['stats']['world_name'] }} : {{ server['stats']['world_size'] }}
|
||||
</td>
|
||||
<td>
|
||||
<td id="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 />
|
||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max',
|
||||
data['lang']) }} <br />
|
||||
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
{{ server['stats']['desc'] }} <br />
|
||||
{% end %}
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
{{ server['stats']['desc'] }} <br />
|
||||
{% end %}
|
||||
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<td id="server_running_status_{{server['server_data']['server_id']}}">
|
||||
{% if server['stats']['running'] %}
|
||||
<i class="fas fa-thumbs-up"></i> <span class="text-success">{{ translate('dashboard', 'online', data['lang']) }}</span>
|
||||
<i class="fas fa-thumbs-up"></i> <span class="text-success">{{ translate('dashboard', 'online',
|
||||
data['lang']) }}</span>
|
||||
{% else %}
|
||||
<i class="fas fa-thumbs-down"></i> <span class="text-danger">{{ translate('dashboard', 'offline', data['lang']) }}</span>
|
||||
<i class="fas fa-thumbs-down"></i> <span class="text-danger">{{ translate('dashboard', 'offline',
|
||||
data['lang']) }}</span>
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -212,192 +244,303 @@
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
<style>
|
||||
.popover-body{
|
||||
color: white !important;;
|
||||
}
|
||||
.popover-body {
|
||||
color: white !important;
|
||||
;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script src="/static/assets/js/motd.js"></script>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="popover"]').popover();
|
||||
if($(window).width() < 1000){
|
||||
$('.too_small').popover("show");
|
||||
function display_motd() {
|
||||
var all_motds = Array.from(document.getElementsByClassName('input_motd'));
|
||||
for (element of all_motds) {
|
||||
initParser(element.id, element.id);
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
$(window).ready(function(){
|
||||
$('body').click(function(){
|
||||
$('.too_small').popover("hide");
|
||||
|
||||
$(document).ready(function () {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
if ($(window).width() < 1000) {
|
||||
$('.too_small').popover("show");
|
||||
}
|
||||
|
||||
});
|
||||
$(window).ready(function () {
|
||||
$('body').click(function () {
|
||||
$('.too_small').popover("hide");
|
||||
});
|
||||
});
|
||||
$(window).resize(function() {
|
||||
// This will execute whenever the window is resized
|
||||
if($(window).width() < 1000){
|
||||
$('.too_small').popover("show");
|
||||
}
|
||||
else{
|
||||
$('.too_small').popover("hide");
|
||||
} // New width
|
||||
});
|
||||
});
|
||||
$(window).resize(function () {
|
||||
// This will execute whenever the window is resized
|
||||
if ($(window).width() < 1000) {
|
||||
$('.too_small').popover("show");
|
||||
}
|
||||
else {
|
||||
$('.too_small').popover("hide");
|
||||
} // New width
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
|
||||
function send_command (server_id, command){
|
||||
/* this getCookie function is in base.html */
|
||||
var token = getCookie("_xsrf");
|
||||
function send_command(server_id, command) {
|
||||
/* this getCookie function is in base.html */
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
setTimeout(function(){
|
||||
if (command != 'start_server'){
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
/*setTimeout(function () {
|
||||
if (command != 'start_server') {
|
||||
location.reload();
|
||||
}
|
||||
}, 10000);
|
||||
}, 10000);*/
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function send_kill(server_id) {
|
||||
/* this getCookie function is in base.html */
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/ajax/kill?id=' + server_id,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
/*setTimeout(function () {
|
||||
location.reload();
|
||||
}, 10000);*/
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update_one_server_status(server) {
|
||||
server_cpu = document.getElementById('server_cpu_' + server.id);
|
||||
server_mem = document.getElementById('server_mem_' + server.id);
|
||||
server_world = document.getElementById('server_world_' + server.id);
|
||||
server_desc = document.getElementById('server_desc_' + server.id);
|
||||
server_online_status = document.getElementById('server_running_status_' + server.id);
|
||||
|
||||
console.log("Received Data : " + server.id + ": " + server);
|
||||
/* TODO Update each element */
|
||||
|
||||
/* Update CPU */
|
||||
cpu_status = "";
|
||||
if (server.cpu <= 33)
|
||||
{
|
||||
cpu_status = "bg-success";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function send_kill (server_id){
|
||||
/* this getCookie function is in base.html */
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/kill?id=' + server_id,
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
setTimeout(function(){
|
||||
location.reload();
|
||||
}, 10000);
|
||||
|
||||
else if (server.cpu > 33 && server.cpu <= 66)
|
||||
{
|
||||
cpu_status = "bg-warning";
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_status = "bg-danger";
|
||||
}
|
||||
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 +`%`;
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log('ready for JS!')
|
||||
|
||||
$( ".play_button" ).click(function() {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'start_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStart", data['lang']) %} </div>'
|
||||
/* Update Memory */
|
||||
mem_status = "";
|
||||
total_mem = "";
|
||||
if (server.mem_percent <= 33)
|
||||
{
|
||||
mem_status = "bg-success";
|
||||
} else if (server.mem_percent > 33 && server.mem_percent <= 66)
|
||||
{
|
||||
mem_status = "bg-warning";
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_status = "bg-danger";
|
||||
}
|
||||
|
||||
if (server.mem == 0)
|
||||
{
|
||||
total_mem = "0 MB";
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
/* Update World Infos */
|
||||
server_world.innerHTML = server.world_name + ` : ` + server.world_size
|
||||
|
||||
/* Update Server Infos */
|
||||
if (server.int_ping_results) {
|
||||
/* Update Players */
|
||||
if (server.players) {
|
||||
server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />`
|
||||
|
||||
server_infos = "";
|
||||
server_infos = server.online + " / " + server.max + "{{ translate('dashboard', 'max', data['lang']) }}<br />"
|
||||
}
|
||||
|
||||
/* Update Motd */
|
||||
var motd = "";
|
||||
if (server.desc) {
|
||||
motd = `<span id="input_motd_` + server.id + `" class="input_motd">` + server.desc + `</span>`;
|
||||
server_infos = server_infos + motd + "<br />";
|
||||
}
|
||||
|
||||
/* Version */
|
||||
if (server.version) {
|
||||
server_infos = server_infos + server.version
|
||||
}
|
||||
server_desc.innerHTML = server_infos;
|
||||
}
|
||||
|
||||
/* Update Online Status */
|
||||
var online_status = "";
|
||||
if (server.running) {
|
||||
online_status = `<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])}}</span>`;
|
||||
}
|
||||
else {
|
||||
online_status = `<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])}}</span>`;
|
||||
}
|
||||
server_online_status.innerHTML = online_status;
|
||||
}
|
||||
|
||||
function update_servers_status(data) {
|
||||
console.log(data);
|
||||
for (server of data) {
|
||||
update_one_server_status(server);
|
||||
}
|
||||
display_motd();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log('ready for JS!')
|
||||
|
||||
$(".play_button").click(function () {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'start_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStart", data['lang']) %} </div>'
|
||||
});
|
||||
});
|
||||
|
||||
$( ".stop_button" ).click(function() {
|
||||
console.log("stopping server");
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'stop_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStop", data['lang']) %} </div>'
|
||||
});
|
||||
});
|
||||
|
||||
$( ".restart_button" ).click(function() {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'restart_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientRestart", data['lang']) %} </div>'
|
||||
$(".stop_button").click(function () {
|
||||
console.log("stopping server");
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'stop_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStop", data['lang']) %} </div>'
|
||||
});
|
||||
});
|
||||
$( ".kill_button" ).click(function() {
|
||||
});
|
||||
|
||||
$(".restart_button").click(function () {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'restart_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientRestart", data['lang']) %} </div>'
|
||||
});
|
||||
});
|
||||
$(".kill_button").click(function () {
|
||||
server_id = $(this).attr("data-id");
|
||||
bootbox.confirm({
|
||||
message: "This will kill the server process and all it's subprocesses. Killing a process can potentially corrupt files. Only do this in extreme circumstances. Are you sure you would like to continue?",
|
||||
buttons: {
|
||||
confirm: {
|
||||
message: "This will kill the server process and all it's subprocesses. Killing a process can potentially corrupt files. Only do this in extreme circumstances. Are you sure you would like to continue?",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: '{% raw translate("dashboard", "kill", data['lang']) %}',
|
||||
className: 'btn-danger'
|
||||
},
|
||||
cancel: {
|
||||
},
|
||||
cancel: {
|
||||
label: '{% raw translate("panelConfig", "cancel", data['lang']) %}',
|
||||
className: 'btn-secondary'
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
if(result){
|
||||
send_kill(server_id);
|
||||
var dialog = bootbox.dialog({
|
||||
title: '{% raw translate("dashboard", "killing", data['lang']) %}',
|
||||
message: '<p><i class="fa fa-spin fa-spinner"></i> Loading...</p>'
|
||||
});
|
||||
|
||||
dialog.init(function(){
|
||||
setTimeout(function(){
|
||||
location.reload();
|
||||
}, 15000);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (webSocket) {
|
||||
cpu_data = document.getElementById('cpu_data');
|
||||
cpu_usage = document.getElementById('cpu_usage');
|
||||
mem_usage = document.getElementById('mem_usage');
|
||||
mem_percent = document.getElementById('mem_percent');
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
send_kill(server_id);
|
||||
var dialog = bootbox.dialog({
|
||||
title: '{% raw translate("dashboard", "killing", data['lang']) %}',
|
||||
message: '<p><i class="fa fa-spin fa-spinner"></i> Loading...</p>'
|
||||
});
|
||||
|
||||
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}`;
|
||||
cpu_data.setAttribute('data-original-title', cpuDataTitle);
|
||||
cpu_usage.textContent = hostStats.cpu_usage;
|
||||
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
||||
mem_percent.textContent = hostStats.mem_percent + '%';
|
||||
dialog.init(function () {
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 15000);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (webSocket) {
|
||||
cpu_data = document.getElementById('cpu_data');
|
||||
cpu_usage = document.getElementById('cpu_usage');
|
||||
mem_usage = document.getElementById('mem_usage');
|
||||
mem_percent = document.getElementById('mem_percent');
|
||||
|
||||
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}`;
|
||||
cpu_data.setAttribute('data-original-title', cpuDataTitle);
|
||||
cpu_usage.textContent = hostStats.cpu_usage;
|
||||
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
||||
mem_percent.textContent = hostStats.mem_percent + '%';
|
||||
});
|
||||
}
|
||||
|
||||
if (webSocket) {
|
||||
webSocket.on('send_start_reload', function () {
|
||||
location.reload()
|
||||
});
|
||||
}
|
||||
webSocket.on('send_start_reload', function () {
|
||||
location.reload()
|
||||
});
|
||||
}
|
||||
|
||||
if (webSocket) {
|
||||
webSocket.on('update_button_status', function (updateButton) {
|
||||
var id = 'controls';
|
||||
var dataId = updateButton.server_id;
|
||||
var string = updateButton.string
|
||||
var id = id.concat(updateButton.server_id);
|
||||
if (updateButton.isUpdating){
|
||||
console.log(updateButton.isUpdating)
|
||||
document.getElementById(id).innerHTML = string;
|
||||
}
|
||||
else{
|
||||
window.location.reload()
|
||||
if (webSocket) {
|
||||
webSocket.on('update_button_status', function (updateButton) {
|
||||
var id = 'controls';
|
||||
var dataId = updateButton.server_id;
|
||||
var string = updateButton.string
|
||||
var id = id.concat(updateButton.server_id);
|
||||
if (updateButton.isUpdating) {
|
||||
console.log(updateButton.isUpdating)
|
||||
document.getElementById(id).innerHTML = string;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
window.location.reload()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$( ".clone_button" ).click(function() {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'clone_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
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>'
|
||||
if (webSocket) {
|
||||
webSocket.on('update_server_status', update_servers_status);
|
||||
}
|
||||
|
||||
$(".clone_button").click(function () {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'clone_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
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>'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
{% end %}
|
@ -1,10 +1,9 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Panel Config{% end %}
|
||||
{% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@ -14,7 +13,8 @@
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">Panel Config</h4>
|
||||
<!-- TODO: Translate the following -->
|
||||
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -30,22 +30,23 @@
|
||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||
<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> Users</h4>
|
||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang']) }}</h4>
|
||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
||||
|
||||
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> Add New User</a></div>
|
||||
<!-- 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>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<!-- TODO: Translate the following -->
|
||||
<tr class="rounded">
|
||||
<th>User</th>
|
||||
<th>Enabled</th>
|
||||
<th>API Token</th>
|
||||
<th>Allowed Servers</th>
|
||||
<th>Assigned Roles</th>
|
||||
<th>Edit</th>
|
||||
<th>{{ translate('panelConfig', 'user', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'enabled', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'allowedServers', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'assignedRoles', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'edit', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -64,9 +65,6 @@
|
||||
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
<button data-toggle="tooltip" title="Show API Key" data-id="{{ user.api_token }}" type="button" class="btn btn-info show_button">Show</button>
|
||||
</td>
|
||||
<td id="server_list_{{user.user_id}}">
|
||||
<ul id="{{user.user_id}}">
|
||||
{% for item in data['auth-servers'][user.user_id] %}
|
||||
@ -95,19 +93,20 @@
|
||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||
<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> Roles</h4>
|
||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles', data['lang']) }}</h4>
|
||||
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
|
||||
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> Add New Role</a></div>
|
||||
<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 class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<!-- TODO: Translate the following -->
|
||||
<tr class="rounded">
|
||||
<th>Role</th>
|
||||
<th>Allowed Servers</th>
|
||||
<th>Role Users</th>
|
||||
<th>Edit</th>
|
||||
<th>{{ translate('panelConfig', 'role', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'allowedServers', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'roleUsers', data['lang']) }}</th>
|
||||
<th>{{ translate('panelConfig', 'edit', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -1,10 +1,9 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit Role{% end %}
|
||||
{% block title %}Crafty Controller - {{ translate('rolesConfig', 'pageTitle', data['lang']) }}{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@ -16,13 +15,13 @@
|
||||
<div class="page-header">
|
||||
{% if data['new_role'] %}
|
||||
<h4 class="page-title">
|
||||
New Role
|
||||
{{ translate('rolesConfig', 'pageTitleNew', data['lang']) }}
|
||||
<br />
|
||||
<small>RID: N/A</small>
|
||||
</h4>
|
||||
{% else %}
|
||||
<h4 class="page-title">
|
||||
Edit Role - {{ data['role']['role_name'] }}
|
||||
{{ translate('rolesConfig', 'pageTitle', data['lang']) }} - {{ data['role']['role_name'] }}
|
||||
<br />
|
||||
<small>RID: {{ data['role']['role_id'] }}</small>
|
||||
</h4>
|
||||
@ -41,7 +40,7 @@
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<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">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
<i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a>
|
||||
</li>
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false">
|
||||
@ -58,22 +57,22 @@
|
||||
{% 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-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> Role Settings</h4>
|
||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="role_name">Role Name <small class="text-muted ml-1"> - What you wish to call this role</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" >
|
||||
</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> Allowed Servers <small class="text-muted ml-1"> - servers this role is allowed to access </small> </h4>
|
||||
<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">
|
||||
@ -81,8 +80,8 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Server Name</th>
|
||||
<th>Access?</th>
|
||||
<th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
|
||||
<th>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -105,19 +104,19 @@
|
||||
</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> Roles Permissions <small class="text-muted ml-1"> - permissions this role has on this/these servers </small></h4>
|
||||
<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="card-body">
|
||||
<div class="form-group">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Permission Name</th>
|
||||
<th>Authorized ?</th>
|
||||
<th>{{ translate('rolesConfig', 'permName', data['lang']) }}</th>
|
||||
<th>{{ translate('rolesConfig', 'permAccess', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -149,14 +148,14 @@
|
||||
<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> Users Assigned to Role:</h4>
|
||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>User Name</th>
|
||||
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -183,22 +182,22 @@
|
||||
<div class="col-md-3 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Role Config Area</h4>
|
||||
<p class="card-description"> Here is where you can change the configuration of your role</p>
|
||||
<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">
|
||||
Created: {{ str(data['role']['created']) }}
|
||||
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
|
||||
<br />
|
||||
Last updated: {{ str(data['role']['last_update']) }}
|
||||
{{ 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> Delete Role</a><br />
|
||||
<small>You cannot delete something that does not yet exist</small>
|
||||
<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> Delete Role</a>
|
||||
<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>
|
||||
@ -229,7 +228,6 @@
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit User{% end %}
|
||||
@ -16,13 +15,13 @@
|
||||
<div class="page-header">
|
||||
{% if data['new_user'] %}
|
||||
<h4 class="page-title">
|
||||
New User
|
||||
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
|
||||
<br />
|
||||
<small>UID: N/A</small>
|
||||
</h4>
|
||||
{% else %}
|
||||
<h4 class="page-title">
|
||||
Edit User - {{ data['user']['user_id'] }}
|
||||
{{ translate('userConfig', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
|
||||
<br />
|
||||
<small>UID: {{ data['user']['user_id'] }}</small>
|
||||
</h4>
|
||||
@ -41,12 +40,12 @@
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<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">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }} - {{ data['user']['user_id'] }}</a>
|
||||
</li>
|
||||
{% if not data['new_user'] %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/add_user?id={{ data['user']['user_id'] }}&subpage=other" role="tab" aria-selected="false">
|
||||
<i class="fas fa-folder-tree"></i>Other</a>
|
||||
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab" aria-selected="false">
|
||||
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }} - {{ data['user']['user_id'] }}</a>
|
||||
</li>
|
||||
{% end %}
|
||||
</ul>
|
||||
@ -65,27 +64,27 @@
|
||||
|
||||
<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"></i> User Settings</h4>
|
||||
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings', data['lang']) }} - {{ data['user']['user_id'] }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">User Name <small class="text-muted ml-1"> - What you wish to call this user</small> </label>
|
||||
<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>
|
||||
<input type="text" class="form-control" name="username" id="username" value="{{ data['user']['username'] }}" placeholder="User Name" >
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password0">Password <small class="text-muted ml-1"> - leave blank to don't change</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="password" class="form-control" name="password0" id="password0" value="" placeholder="Password" >
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password1">Repeat Password <small class="text-muted ml-1"> - leave blank to don't change</small> </label>
|
||||
<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">Gravatar Email <small class="text-muted ml-1"> - for the profile picture. this is not required. crafty will never make use of user emails. User emails are strictly for Gravatar</small> </label>
|
||||
<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">User Language:</label>
|
||||
<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'] %}
|
||||
<option value="{{lang}}">{{lang}}</option>
|
||||
@ -97,7 +96,7 @@
|
||||
|
||||
<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> Roles <small class="text-muted ml-1"> - the roles this user is a member of</small></h4>
|
||||
<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 class="card-body">
|
||||
<div class="form-group">
|
||||
@ -105,8 +104,8 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Role Name</th>
|
||||
<th>Member?</th>
|
||||
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
|
||||
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -134,7 +133,7 @@
|
||||
|
||||
<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> Crafty Permissions <small class="text-muted ml-1"> - permissions this user has on Crafty Controller </small></h4>
|
||||
<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']) }}permissions this user has on Crafty Controller </small></h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
@ -142,9 +141,9 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Permission Name</th>
|
||||
<th>Authorized ?</th>
|
||||
<th>Number of Uses Allowed (-1=No Limit)</th>
|
||||
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
|
||||
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
|
||||
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -171,25 +170,17 @@
|
||||
<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">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">Enabled
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="regen_api" class="form-check-label ml-4 mb-4">
|
||||
{% if data['new_user'] %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" checked="" value="1" disabled >Regenerate API Key
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" value="1">Regenerate API Key
|
||||
<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'] }} >Super User
|
||||
<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" >Super User
|
||||
<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>
|
||||
|
||||
@ -203,19 +194,17 @@
|
||||
<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> User Config Area</h4>
|
||||
<p class="card-description"> Here is where you can change the configuration of your user</p>
|
||||
<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">
|
||||
Created: {{ str(data['user']['created']) }}
|
||||
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
|
||||
<br />
|
||||
Last login: {{ str(data['user']['last_login']) }}
|
||||
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
|
||||
<br />
|
||||
Last update: {{ str(data['user']['last_update']) }}
|
||||
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
|
||||
<br />
|
||||
Last IP: {{ data['user']['last_ip'] }}
|
||||
<br />
|
||||
API Key: {{ data['user']['api_token'] }}
|
||||
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
|
||||
<br />
|
||||
</p>
|
||||
</blockquote>
|
||||
@ -223,13 +212,13 @@
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{% if data['new_user'] %}
|
||||
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> Delete User</a><br />
|
||||
<small>You cannot delete something that does not yet exist</small>
|
||||
<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> Delete User</a><br />
|
||||
<small>You cannot delete a superuser</small>
|
||||
<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> Delete User</a>
|
||||
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
@ -256,8 +245,8 @@
|
||||
console.log("User to delete is {{ data['user']['username'] }}");
|
||||
|
||||
bootbox.confirm({
|
||||
title: "{% raw translate('usersConfig', 'deleteUser', data['lang']) %}"+"{{ data['user']['username'] }}",
|
||||
message: "{{ translate('usersConfig', 'confirmDelete', data['lang']) }}",
|
||||
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %}"+"{{ data['user']['username'] }}",
|
||||
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
|
||||
|
252
app/frontend/templates/panel/panel_edit_user_apikeys.html
Normal file
252
app/frontend/templates/panel/panel_edit_user_apikeys.html
Normal file
@ -0,0 +1,252 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit User API Keys{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
{{ translate('apiKeys', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
|
||||
<br/>
|
||||
<small>UID: {{ data['user']['user_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=config"
|
||||
role="tab"
|
||||
aria-selected="false">
|
||||
<i class="fas fa-cogs"></i>{{ translate('apiKeys', 'config', data['lang']) }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}"
|
||||
role="tab"
|
||||
aria-selected="true">
|
||||
<i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7 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-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<!--<th>ID</th>-->
|
||||
<th>{{ translate('apiKeys', 'name', data['lang']) }}</th>
|
||||
<th>{{ translate('apiKeys', 'created', data['lang']) }}</th>
|
||||
<th>{{ translate('apiKeys', 'superUser', data['lang']) }}</th>
|
||||
<th>{{ translate('apiKeys', 'perms', data['lang']) }}</th>
|
||||
<th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for apikey in data['api_keys'] %}
|
||||
<tr>
|
||||
<!--<td>{-{ apikey.token_id }-}</td>-->
|
||||
<td>{{ apikey.name }}</td>
|
||||
<td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td>
|
||||
<td>
|
||||
{% if apikey.superuser %}
|
||||
<span class="text-success">
|
||||
<i class="fas fa-check-square"></i> {{ translate('apiKeys', 'yes', data['lang']) }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
<i class="far fa-times-square"></i> {{ translate('apiKeys', 'no', data['lang']) }}
|
||||
</span>
|
||||
{% end %}
|
||||
</td>
|
||||
<td>{{ translate('apiKeys', 'server', data['lang']) }} {{ apikey.server_permissions }}
|
||||
{{ translate('apiKeys', 'crafty', data['lang']) }} {{ apikey.crafty_permissions }}</td>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-danger delete-api-key"
|
||||
data-key-id="{{ apikey.token_id }}"
|
||||
data-key-name="{{ apikey.name }}"
|
||||
>{{ translate('panelConfig', 'delete', data['lang']) }}</button>
|
||||
<button
|
||||
class="btn btn-outline-primary get-a-token"
|
||||
data-key-id="{{ apikey.token_id }}"
|
||||
data-key-name="{{ apikey.name }}"
|
||||
>{{ translate('apiKeys', 'getToken', data['lang']) }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5 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-plus"></i> {{ translate('apiKeys', 'createNew', data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="user_form" class="forms-sample" method="post"
|
||||
action="/panel/edit_user_apikeys">
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">{{ translate('apiKeys', 'name', data['lang']) }}<small
|
||||
class="text-muted ml-1"> - {{ translate('apiKeys', 'nameDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
placeholder="API Key">
|
||||
</div>
|
||||
|
||||
<table class="table table-hover mb-3">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>{{ translate('apiKeys', 'permName', data['lang']) }}</th>
|
||||
<th>{{ translate('apiKeys', 'auth', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for permission in data['server_permissions_all'] %}
|
||||
<tr>
|
||||
<td><label
|
||||
for="permission_{{ permission.name }}">{{ permission.name }}</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" class=""
|
||||
id="permission_{{ permission.name }}"
|
||||
name="permission_{{ permission.name }}" value="1">
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
{% for permission in data['crafty_permissions_all'] %}
|
||||
<tr>
|
||||
<td><label
|
||||
for="permission_{{ permission.name }}">{{ permission.name }}</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" class=""
|
||||
id="permission_{{ permission.name }}"
|
||||
name="permission_{{ permission.name }}" value="1">
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<label for="superuser">Superuser</label>
|
||||
<input type="checkbox" class="" id="superuser"
|
||||
name="superuser" value="1">
|
||||
|
||||
<br/>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-plus"></i>
|
||||
Create
|
||||
</button>
|
||||
<button type="reset" class="btn btn-light"><i
|
||||
class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
$('.delete-api-key').click(function () {
|
||||
var keyId = $(this).data("key-id");
|
||||
var keyName = $(this).data("key-name");
|
||||
bootbox.confirm({
|
||||
title: `Remove API key ${keyName}?`,
|
||||
message: "Do you want to delete this API key? This cannot be undone.",
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: '<i class="fas fa-times"></i> {{ translate("panelConfig", "cancel", data['lang']) }}'
|
||||
},
|
||||
confirm: {
|
||||
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/panel/remove_apikey?id=' + keyId,
|
||||
success: function (data) {
|
||||
location.reload();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
$('.get-a-token').click(function () {
|
||||
var keyId = $(this).data("key-id");
|
||||
var keyName = $(this).data("key-name");
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/panel/get_token?id=' + keyId,
|
||||
success: function (data) {
|
||||
bootbox.alert({
|
||||
title: `API token for ${keyName}`,
|
||||
message: `Here is an API token for ${keyName}:\n<pre style="white-space: pre-wrap;color:white;word-break:break-all;background: grey;border-radius: 5px;">${data}</pre>`
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
@ -1,56 +1,52 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-3 pb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mr-2">
|
||||
{% if data['server_stats']['running'] %}
|
||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span class="text-success">{{ translate('serverStats', 'online', data['lang']) }}</span><br />
|
||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started">{{ data['server_stats']['started'] }} ({{ translate('serverStats', 'serverTime', data['lang']) }})</span><br />
|
||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{ translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span>
|
||||
{% end %}
|
||||
<br>
|
||||
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{ data['serverTZ'] }}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 mr-2">
|
||||
<b>{{ translate('serverStats', 'cpuUsage', data['lang']) }}:</b> {{ data['server_stats']['cpu'] }}% <br />
|
||||
<b>{{ translate('serverStats', 'memUsage', data['lang']) }}:</b> {{ data['server_stats']['mem'] }} <br />
|
||||
{% if data['server_stats']['int_ping_results'] %}
|
||||
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> {{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}<br />
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> 0/0<br />
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 mr-2">
|
||||
{% if data['server_stats']['version'] != 'False' %}
|
||||
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> {{ data['server_stats']['version'] }} <br />
|
||||
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd" class="input_motd">{{ data['server_stats']['desc'] }}</span> <br />
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> {{ translate('serverStats', 'unableToConnect', data['lang']) }} <br />
|
||||
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> {{ translate('serverStats', 'unableToConnect', data['lang']) }} <br />
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-3 pb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mr-2">
|
||||
{% 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', 'serverStarted', data['lang']) }}:</b> <span id="started">{{ data['server_stats']['started'] }} ({{ translate('serverStats', 'serverTime', data['lang']) }})</span><br />
|
||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{ translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started" class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
|
||||
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-danger">{{ translate('serverStats', 'offline', data['lang']) }}</span>
|
||||
{% end %}
|
||||
<br>
|
||||
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{ data['serverTZ'] }}</span>
|
||||
</div>
|
||||
|
||||
<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', 'memUsage', data['lang']) }}:</b> <span id="mem" >{{ data['server_stats']['mem'] }}</span> <br />
|
||||
{% 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 />
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players" >0/0</span><br />
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 mr-2">
|
||||
{% 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', 'description', data['lang']) }}:</b> <span id="input_motd" class="input_motd">{{ data['server_stats']['desc'] }}</span> <br />
|
||||
{% else %}
|
||||
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
|
||||
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd" class="input_motd">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/static/assets/vendors/moment/moment.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="/static/assets/js/motd.js"></script>
|
||||
<script>
|
||||
|
||||
function durationToHumanizedString (duration) {
|
||||
function durationToHumanizedString(duration) {
|
||||
duration._data.months += duration._data.years * 12;
|
||||
// 30.45833333333 = average month length, calculate with (31+28.5+31+30+31+30+31+31+30+31+30+31) / 12
|
||||
duration._data.days += duration._data.months * 30.45833333333;
|
||||
@ -64,7 +60,7 @@
|
||||
|
||||
output = Object.entries(obj)
|
||||
.map(([type, num]) => {
|
||||
// make them strings
|
||||
// make them strings
|
||||
returnData = num + ' ' + type;
|
||||
// remove the s in the end if the data is -1 or 1
|
||||
if (num == -1 || num == 1)
|
||||
@ -73,27 +69,28 @@
|
||||
})
|
||||
.map((v, i, a) => // example input: [1,2,3], output: "1, 2 and 3"
|
||||
v + (i !== a.length - 1
|
||||
? i !== a.length - 2
|
||||
? i !== a.length - 2
|
||||
? ', '
|
||||
: ' and '
|
||||
: '')).join('');
|
||||
return output;
|
||||
}
|
||||
|
||||
let uptime = document.querySelector('#uptime');
|
||||
let started = document.querySelector('#started');
|
||||
let startedUTC;
|
||||
let startedLocal;
|
||||
let uptimeLoop;
|
||||
|
||||
document.body.onload = (() => {
|
||||
|
||||
console.log('calculateTime');
|
||||
let uptime = document.querySelector('#uptime');
|
||||
let started = document.querySelector('#started');
|
||||
let startedUTC;
|
||||
let startedLocal;
|
||||
|
||||
if (started != null) {
|
||||
startedUTC = '{{ data['server_stats']['started'] }}';
|
||||
startedUTC = '{{ data['server_stats']['started'] }}';
|
||||
if (startedUTC != 'False') {
|
||||
console.log('started utc:', startedUTC);
|
||||
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
let browserUTCOffset = moment().utcOffset(); // This is in minutes
|
||||
var browserUTCOffset = moment().utcOffset(); // This is in minutes
|
||||
|
||||
startedLocal = startedUTC.utcOffset(browserUTCOffset);
|
||||
startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss');
|
||||
@ -104,23 +101,108 @@
|
||||
}
|
||||
|
||||
var calculateUptime = () => {
|
||||
var msdiff = moment()
|
||||
.diff(startedLocal);
|
||||
var msdiff = moment().diff(startedLocal);
|
||||
var diff = moment.duration(msdiff);
|
||||
|
||||
uptime.textContent = durationToHumanizedString(diff);
|
||||
}
|
||||
|
||||
if (uptime != null && started != null) {
|
||||
|
||||
console.log('startedLocal', startedLocal)
|
||||
if (startedLocal) {
|
||||
calculateUptime()
|
||||
var uptimeLoop = setInterval(calculateUptime, 1000)
|
||||
calculateUptime();
|
||||
uptimeLoop = setInterval(calculateUptime, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
initParser('input_motd', 'input_motd');
|
||||
|
||||
});
|
||||
|
||||
function update_server_details(server) {
|
||||
server_status = document.getElementById('status');
|
||||
server_started = document.getElementById('started');
|
||||
server_uptime = document.getElementById('uptime');
|
||||
server_cpu = document.getElementById('cpu');
|
||||
server_mem = document.getElementById('mem');
|
||||
server_players = document.getElementById('players');
|
||||
server_version = document.getElementById('version');
|
||||
server_input_motd = document.getElementById('input_motd');
|
||||
|
||||
/* TODO Update each element */
|
||||
if (server.running)
|
||||
{
|
||||
if (server.int_ping_results)
|
||||
{
|
||||
server_status.setAttribute("class", "text-success");
|
||||
server_status.innerHTML = `{{ translate('serverStats', 'online', data['lang']) }}`;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_status.setAttribute("class", "text-warning");
|
||||
server_status.innerHTML = `{{ translate('serverStats', 'starting', data['lang']) }}`;
|
||||
}
|
||||
|
||||
startedUTC = server.started;
|
||||
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
||||
var browserUTCOffset = moment().utcOffset(); // This is in minutes
|
||||
startedLocal = startedUTC.utcOffset(browserUTCOffset);
|
||||
startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss');
|
||||
server_started.setAttribute("class", "");
|
||||
server_started.innerHTML = startedLocalFormatted +` ({{ translate('serverStats', 'serverTime', data['lang']) }})`;
|
||||
server_uptime.setAttribute("class", "");
|
||||
if (!uptimeLoop) {
|
||||
var calculateUptime = () => {
|
||||
var msdiff = moment().diff(startedLocal);
|
||||
var diff = moment.duration(msdiff);
|
||||
uptime.textContent = durationToHumanizedString(diff);
|
||||
}
|
||||
uptimeLoop = setInterval(calculateUptime, 1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
server_status.setAttribute("class", "text-danger");
|
||||
server_status.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||
server_started.setAttribute("class", "text-danger");
|
||||
server_started.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||
clearInterval(uptimeLoop);
|
||||
uptimeLoop = null;
|
||||
server_uptime.setAttribute("class", "text-danger");
|
||||
server_uptime.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
|
||||
}
|
||||
|
||||
server_cpu.innerHTML = server.cpu + ` %`;
|
||||
server_mem.innerHTML = server.mem;
|
||||
|
||||
if (server.int_ping_results)
|
||||
{
|
||||
server_players.innerHTML = server.online + `/` + server.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_players.innerHTML = `0/0`;
|
||||
}
|
||||
|
||||
if (server.version)
|
||||
{
|
||||
server_version.innerHTML = server.version;
|
||||
server_input_motd.innerHTML = server.desc;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_version.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
||||
server_input_motd.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
|
||||
}
|
||||
|
||||
initParser('input_motd', 'input_motd');
|
||||
|
||||
}
|
||||
|
||||
$(window).ready(function () {
|
||||
console.log("ready!");
|
||||
|
||||
//if (webSocket) {
|
||||
webSocket.on('update_server_details', update_server_details);
|
||||
//}
|
||||
});
|
||||
</script>
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -33,7 +32,7 @@
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<style>
|
||||
@ -86,7 +85,7 @@
|
||||
<li class="playerItem banned">
|
||||
<h3>{{ translate('serverPlayerManagement', 'loadingBannedPlayers', data['lang']) }}</h3>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -65,7 +64,7 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">{{ translate('serverBackups', 'currentBackups', data['lang']) }}</h4>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
@ -231,7 +230,7 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$( ".restore_button" ).click(function() {
|
||||
var file_to_restore = $(this).data("file");
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -10,14 +9,15 @@
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,132 +25,187 @@
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
{% include "parts/details_stats.html" %}
|
||||
{% include "parts/details_stats.html" %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
{% include "parts/server_controls_list.html %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<form class="forms-sample" method="post" action="/panel/server_detail">
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
||||
<input type="hidden" name="subpage" value="config">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<form class="forms-sample" method="post" action="/panel/server_detail">
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
||||
<input type="hidden" name="subpage" value="config">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_name">{{ translate('serverConfig', 'serverName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverNameDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="server_name" id="server_name" value="{{ data['server_stats']['server_id']['server_name'] }}" placeholder="{{ translate('serverConfig', 'serverName', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{% if data['super_user'] %}
|
||||
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="{{ translate('serverConfig', 'serverPath', data['lang']) }}" required>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="log_path">{{ translate('serverConfig', 'serverLogLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverLogLocationDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="log_path" id="log_path" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="{{ translate('serverConfig', 'serverLogLocation', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="executable">{{ translate('serverConfig', 'serverExecutable', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutableDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="executable" id="executable" value="{{ data['server_stats']['server_id']['executable'] }}" placeholder="{{ translate('serverConfig', 'serverExecutable', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="execution_command" id="execution_command" value="{{ data['server_stats']['server_id']['execution_command'] }}" placeholder="{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}" required>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverStopCommandDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="stop_command" id="stop_command" value="{{ data['server_stats']['server_id']['stop_command'] }}" placeholder="{{ translate('serverConfig', 'serverStopCommand', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="auto_start_delay">{{ translate('serverConfig', 'serverAutostartDelay', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverAutostartDelayDesc', data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay" value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10" required>
|
||||
</div>
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
<label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="executable_update_url" id="executable_update_url" value="{{ data['server_stats']['server_id']['executable_update_url'] }}" placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small class="text-muted ml-1">- {{ translate('serverConfig', 'serverIPDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="server_ip" id="server_ip" value="{{ data['server_stats']['server_id']['server_ip'] }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_port">{{ translate('serverConfig', 'serverPort', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc', data['lang']) }} </small> </label>
|
||||
<input type="number" class="form-control" name="server_port" id="server_port" value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1" required>
|
||||
</div>
|
||||
{% end %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc', data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" required>
|
||||
</div>
|
||||
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked="" value="1">{{ translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">{{ translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="crash_detection" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['crash_detection'] %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" checked="" value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" value="1" >{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('serverConfig', 'save', data['lang']) }}</button>
|
||||
<button type="reset" class="btn btn-light"><i class="fas fa-times"></i> {{ translate('serverConfig', 'cancel', data['lang']) }}</button>
|
||||
</form>
|
||||
<div class="form-group">
|
||||
<label for="server_name">{{ translate('serverConfig', 'serverName', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverNameDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="server_name" id="server_name"
|
||||
value="{{ data['server_stats']['server_id']['server_name'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverName', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">{{ translate('serverConfigHelp', 'title', data['lang']) }}</h4>
|
||||
<p class="card-description"> {{ translate('serverConfigHelp', 'desc', data['lang']) }}</p>
|
||||
<blockquote class="blockquote">
|
||||
<p class="mb-0">
|
||||
{% raw translate('serverConfigHelp', 'perms', data['lang']) %}
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{% if data['server_stats']['running'] %}
|
||||
<button onclick="send_command(server_id, 'update_executable');" id="update_executable" style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig', 'update', data['lang']) }}</button>
|
||||
<a class="btn btn-sm btn-danger disabled">{{ translate('serverConfig', 'deleteServer', data['lang']) }}</a><br />
|
||||
<small>{{ translate('serverConfig', 'stopBeforeDeleting', data['lang']) }}</small>
|
||||
{% else %}
|
||||
<button onclick="send_command(server_id, 'update_executable');" id="update_executable" style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1">{{ translate('serverConfig', 'update', data['lang']) }}</button>
|
||||
<button onclick="deleteConfirm()" class="btn btn-sm btn-danger">{{ translate('serverConfig', 'deleteServer', data['lang']) }}</button>
|
||||
{% end %}
|
||||
<div class="form-group">
|
||||
{% if data['super_user'] %}
|
||||
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="server_path" id="server_path"
|
||||
value="{{ data['server_stats']['server_id']['path'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverPath', data['lang']) }}" required>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="log_path">{{ translate('serverConfig', 'serverLogLocation', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverLogLocationDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="log_path" id="log_path"
|
||||
value="{{ data['server_stats']['server_id']['log_path'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverLogLocation', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="executable">{{ translate('serverConfig', 'serverExecutable', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutableDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="executable" id="executable"
|
||||
value="{{ data['server_stats']['server_id']['executable'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverExecutable', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="execution_command" id="execution_command"
|
||||
value="{{ data['server_stats']['server_id']['execution_command'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}" required>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverStopCommandDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="stop_command" id="stop_command"
|
||||
value="{{ data['server_stats']['server_id']['stop_command'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverStopCommand', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="auto_start_delay">{{ translate('serverConfig', 'serverAutostartDelay', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverAutostartDelayDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay"
|
||||
value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10"
|
||||
required>
|
||||
</div>
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
<label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="executable_update_url" id="executable_update_url"
|
||||
value="{{ data['server_stats']['server_id']['executable_update_url'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small
|
||||
class="text-muted ml-1">- {{ translate('serverConfig', 'serverIPDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="server_ip" id="server_ip"
|
||||
value="{{ data['server_stats']['server_id']['server_ip'] }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_port">{{ translate('serverConfig', 'serverPort', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc', data['lang']) }}
|
||||
</small> </label>
|
||||
<input type="number" class="form-control" name="server_port" id="server_port"
|
||||
value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1"
|
||||
required>
|
||||
</div>
|
||||
{% end %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after"
|
||||
value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked=""
|
||||
value="1">{{ translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">{{
|
||||
translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="crash_detection" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['crash_detection'] %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
checked="" value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
|
||||
translate('serverConfig', 'save', data['lang']) }}</button>
|
||||
<button type="reset" class="btn btn-light"><i class="fas fa-times"></i> {{ translate('serverConfig',
|
||||
'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">{{ translate('serverConfigHelp', 'title', data['lang']) }}</h4>
|
||||
<p class="card-description"> {{ translate('serverConfigHelp', 'desc', data['lang']) }}</p>
|
||||
<blockquote class="blockquote">
|
||||
<p class="mb-0">
|
||||
{% raw translate('serverConfigHelp', 'perms', data['lang']) %}
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{% if data['server_stats']['running'] %}
|
||||
<button onclick="send_command(server_id, 'update_executable');" id="update_executable"
|
||||
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig',
|
||||
'update', data['lang']) }}</button>
|
||||
<a class="btn btn-sm btn-danger disabled">{{ translate('serverConfig', 'deleteServer', data['lang'])
|
||||
}}</a><br />
|
||||
<small>{{ translate('serverConfig', 'stopBeforeDeleting', data['lang']) }}</small>
|
||||
{% else %}
|
||||
<button onclick="send_command(server_id, 'update_executable');" id="update_executable"
|
||||
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1">{{ translate('serverConfig',
|
||||
'update', data['lang']) }}</button>
|
||||
<button onclick="deleteConfirm()" class="btn btn-sm btn-danger">{{ translate('serverConfig',
|
||||
'deleteServer', data['lang']) }}</button>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -167,154 +222,154 @@
|
||||
<script>
|
||||
|
||||
|
||||
//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 getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
|
||||
});
|
||||
|
||||
function deleteServerE(callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/ajax/delete_server?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: {
|
||||
},
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
|
||||
function deleteServerE(callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/delete_server?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: {
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
function deleteServerFilesE(path, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/delete_server_files?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: {
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
|
||||
function send_command (server_id, command){
|
||||
<!-- this getCookie function is in base.html-->
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
setTimeout(function(){ location.reload(); }, 10000);
|
||||
|
||||
}
|
||||
});
|
||||
if(command != "delete_server" && command != "delete_server_files"){
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingRequest", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientUpdate", data['lang']) %} </div>'
|
||||
}
|
||||
function deleteServerFilesE(path, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/ajax/delete_server_files?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: {
|
||||
},
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
|
||||
function send_command(server_id, command) {
|
||||
//<!-- this getCookie function is in base.html-->
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
setTimeout(function () { location.reload(); }, 10000);
|
||||
}
|
||||
});
|
||||
if (command != "delete_server" && command != "delete_server_files") {
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingRequest", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientUpdate", data['lang']) %} </div>'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteServer (){
|
||||
path = "{{data['server_stats']['server_id']['path']}}";
|
||||
name = "{{data['server_stats']['server_id']['server_name']}}";
|
||||
bootbox.dialog({
|
||||
size: "",
|
||||
title: "{% raw translate('serverConfig', 'deleteFilesQuestion', data['lang']) %}",
|
||||
closeButton: false,
|
||||
message: "{% raw translate('serverConfig', 'deleteFilesQuestionMessage', data['lang']) %}",
|
||||
buttons: {
|
||||
files: {
|
||||
label: "{{ translate('serverConfig', 'yesDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-danger',
|
||||
callback: function(){
|
||||
deleteServerFilesE();
|
||||
setTimeout(function(){ window.location = '/panel/dashboard'; }, 5000);
|
||||
bootbox.dialog({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingDelete", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientDeleteFiles", data['lang']) %} </div>',
|
||||
closeButton: false
|
||||
})
|
||||
function deleteServer() {
|
||||
path = "{{data['server_stats']['server_id']['path']}}";
|
||||
name = "{{data['server_stats']['server_id']['server_name']}}";
|
||||
bootbox.dialog({
|
||||
size: "",
|
||||
title: "{% raw translate('serverConfig', 'deleteFilesQuestion', data['lang']) %}",
|
||||
closeButton: false,
|
||||
message: "{% raw translate('serverConfig', 'deleteFilesQuestionMessage', data['lang']) %}",
|
||||
buttons: {
|
||||
files: {
|
||||
label: "{{ translate('serverConfig', 'yesDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-danger',
|
||||
callback: function () {
|
||||
deleteServerFilesE();
|
||||
setTimeout(function () { window.location = '/panel/dashboard'; }, 5000);
|
||||
bootbox.dialog({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingDelete", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientDeleteFiles", data['lang']) %} </div>',
|
||||
closeButton: false
|
||||
})
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
noFiles: {
|
||||
label: "{{ translate('serverConfig', 'noDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-outline-danger',
|
||||
callback: function(){
|
||||
deleteServerE()
|
||||
setTimeout(function(){ window.location = '/panel/dashboard'; }, 5000);
|
||||
bootbox.dialog({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingDelete", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientDelete", data['lang']) %} </div>',
|
||||
closeButton: false
|
||||
})
|
||||
return;
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
label: "{{ translate('serverConfig', 'cancel', data['lang']) }}",
|
||||
className: 'btn-secondary',
|
||||
callback: function(){
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
|
||||
},
|
||||
noFiles: {
|
||||
label: "{{ translate('serverConfig', 'noDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-outline-danger',
|
||||
callback: function () {
|
||||
deleteServerE()
|
||||
setTimeout(function () { window.location = '/panel/dashboard'; }, 5000);
|
||||
bootbox.dialog({
|
||||
backdrop: true,
|
||||
title: '{% raw translate("serverConfig", "sendingDelete", data['lang']) %}',
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("serverConfig", "bePatientDelete", data['lang']) %} </div>',
|
||||
closeButton: false
|
||||
})
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
function deleteConfirm (){
|
||||
path = "{{data['server_stats']['server_id']['path']}}";
|
||||
name = "{{data['server_stats']['server_id']['server_name']}}";
|
||||
bootbox.confirm({
|
||||
size: "",
|
||||
title: "{% raw translate('serverConfig', 'deleteServerQuestion', data['lang']) %}",
|
||||
closeButton: false,
|
||||
message: "{% raw translate('serverConfig', 'deleteServerQuestionMessage', data['lang']) %}",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: "{{ translate('serverConfig', 'yesDelete', data['lang']) }}",
|
||||
className: 'btn-danger',
|
||||
},
|
||||
cancel: {
|
||||
label: "{{ translate('serverConfig', 'noDelete', data['lang']) }}",
|
||||
className: 'btn-link',
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result){
|
||||
return;
|
||||
return;}
|
||||
else{
|
||||
deleteServer();
|
||||
}
|
||||
|
||||
},
|
||||
cancel: {
|
||||
label: "{{ translate('serverConfig', 'cancel', data['lang']) }}",
|
||||
className: 'btn-secondary',
|
||||
callback: function () {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
function deleteConfirm() {
|
||||
path = "{{data['server_stats']['server_id']['path']}}";
|
||||
name = "{{data['server_stats']['server_id']['server_name']}}";
|
||||
bootbox.confirm({
|
||||
size: "",
|
||||
title: "{% raw translate('serverConfig', 'deleteServerQuestion', data['lang']) %}",
|
||||
closeButton: false,
|
||||
message: "{% raw translate('serverConfig', 'deleteServerQuestionMessage', data['lang']) %}",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: "{{ translate('serverConfig', 'yesDelete', data['lang']) }}",
|
||||
className: 'btn-danger',
|
||||
},
|
||||
cancel: {
|
||||
label: "{{ translate('serverConfig', 'noDelete', data['lang']) }}",
|
||||
className: 'btn-link',
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
if (!result) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
deleteServer();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -557,7 +556,7 @@
|
||||
xmlHttpRequest.addEventListener('error', (e) => {
|
||||
console.error('Error while uploading file', file.name + '.', 'Event:', e)
|
||||
}, false);
|
||||
|
||||
|
||||
xmlHttpRequest.send(file);
|
||||
}
|
||||
|
||||
@ -637,7 +636,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
var fileList = document.getElementById("files");
|
||||
fileList.addEventListener("change", function (e) {
|
||||
@ -672,7 +671,7 @@
|
||||
}catch{
|
||||
document.getElementById('files-tree').innerHTML = text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir);
|
||||
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files');
|
||||
@ -724,7 +723,7 @@
|
||||
setTimeout(function () {setTreeViewContext()}, 1000);
|
||||
|
||||
var toggler = document.getElementById(path);
|
||||
|
||||
|
||||
if (toggler.classList.contains('files-tree-title')){
|
||||
document.getElementById(path+"span").addEventListener("click", function caretListener() {
|
||||
document.getElementById(path+"ul").classList.toggle("d-block");
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -203,9 +202,9 @@ try{
|
||||
}catch{
|
||||
console.log("no element named {{ data['schedule']['action'] }}")
|
||||
}
|
||||
ifDays();
|
||||
yesnoCheck();
|
||||
basicAdvanced();
|
||||
ifDays();
|
||||
if("{{ data['schedule']['enabled'] }}" == 'True'){
|
||||
document.getElementById('enabled').checked = true;
|
||||
}else{
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -205,7 +204,7 @@
|
||||
|
||||
</div>
|
||||
<style>
|
||||
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
td::-webkit-scrollbar {
|
||||
display: none;
|
||||
@ -246,7 +245,7 @@ $( document ).ready(function() {
|
||||
document.getElementById('schedule_table_wrapper').hidden = true;
|
||||
document.getElementById('mini_schedule_table_wrapper').hidden = false;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
$(window).ready(function(){
|
||||
$('body').click(function(){
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %}
|
||||
@ -10,14 +9,14 @@
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,66 +24,67 @@
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
{% include "parts/details_stats.html %}
|
||||
{% include "parts/details_stats.html %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="input-group">
|
||||
<div id="virt_console" class="" style="width: 100%; font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;"></div>
|
||||
</div>
|
||||
<br />
|
||||
{% include "parts/server_controls_list.html %}
|
||||
|
||||
<div style="gap: 0.5rem;" class="input-group flex-wrap">
|
||||
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command" name="server_command" placeholder="{{ translate('serverTerm', 'commandInput', data['lang']) }}" autofocus="">
|
||||
<span class="input-group-btn ml-5">
|
||||
<button id="submit" class="btn btn-sm btn-info" type="button">{{ translate('serverTerm', 'sendCommand', data['lang']) }}</button>
|
||||
</span>
|
||||
</div>
|
||||
{% if data['permissions']['Commands'] in data['user_permissions'] %}
|
||||
{% if data['server_stats']['updating']%}
|
||||
<div id="update_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: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ 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 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>
|
||||
</div>
|
||||
{% elif data['waiting_start'] %}
|
||||
<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: 7rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled" data-toggle="tooltip" title="{{ translate('serverTerm', 'delay-explained', data['lang'])}}">{{ translate('serverTerm', 'starting', 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>
|
||||
</div>
|
||||
{% 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">
|
||||
<button onclick="send_command(server_id, '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(server_id, '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="send_command(server_id, 'stop_server');" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
||||
</div>
|
||||
{% end %}
|
||||
{% end %}
|
||||
<div class="col-md-12">
|
||||
<div class="input-group">
|
||||
<div id="virt_console" class="" style="width: 100%; font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;"></div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<div style="gap: 0.5rem;" class="input-group flex-wrap">
|
||||
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command" name="server_command" placeholder="{{ translate('serverTerm', 'commandInput', data['lang']) }}" autofocus="">
|
||||
<span class="input-group-btn ml-5">
|
||||
<button id="submit" class="btn btn-sm btn-info" type="button">{{ translate('serverTerm', 'sendCommand',
|
||||
data['lang']) }}</button>
|
||||
</span>
|
||||
</div>
|
||||
{% if data['permissions']['Commands'] in data['user_permissions'] %}
|
||||
{% if data['server_stats']['updating']%}
|
||||
<div id="update_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: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ 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 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>
|
||||
</div>
|
||||
{% elif data['waiting_start'] %}
|
||||
<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: 7rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled" data-toggle="tooltip" title="{{ translate('serverTerm', 'delay-explained', data['lang'])}}">{{ translate('serverTerm', 'starting', 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>
|
||||
</div>
|
||||
{% 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">
|
||||
<button onclick="send_command(server_id, '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(server_id, '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="send_command(server_id, 'stop_server');" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
|
||||
</div>
|
||||
{% end %}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
#virt_console::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
#virt_console {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
#virt_console::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
#virt_console {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
</style>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
@ -93,188 +93,182 @@
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
function send_command (server_id, command){
|
||||
if (command == 'start_server'){
|
||||
startBtn.setAttribute('disabled', 'disabled');
|
||||
restartBtn.removeAttribute('disabled');
|
||||
stopBtn.removeAttribute('disabled');
|
||||
}
|
||||
if (command == 'stop_server'){
|
||||
startBtn.removeAttribute('disabled');
|
||||
restartBtn.setAttribute('disabled', 'disabled');
|
||||
stopBtn.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
<!-- this getCookie function is in base.html-->
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
setTimeout(function(){
|
||||
if (command != 'start_server'){
|
||||
location.reload();
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
if (webSocket) {
|
||||
webSocket.on('update_button_status', function (updateButton) {
|
||||
if (updateButton.isUpdating){
|
||||
if(updateButton.server_id == '{{ data['server_stats']['server_id']['server_id'] }}') {
|
||||
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>';
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (updateButton.server_id == '{{ data['server_stats']['server_id']['server_id'] }}') {
|
||||
window.location.reload()
|
||||
document.getElementById('update_control_buttons').innerHTML = '<button onclick="send_command(server_id, "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(server_id, "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
|
||||
// then to boolean via JSON.parse()
|
||||
let online = JSON.parse('{{ data['server_stats']['running'] }}'.toLowerCase());
|
||||
|
||||
let startBtn = document.querySelector('#start-btn');
|
||||
let restartBtn = document.querySelector('#restart-btn');
|
||||
let stopBtn = document.querySelector('#stop-btn');
|
||||
|
||||
{% if data['permissions']['Commands'] in data['user_permissions'] %}
|
||||
if (online) {
|
||||
function send_command(server_id, command) {
|
||||
if (command == 'start_server') {
|
||||
startBtn.setAttribute('disabled', 'disabled');
|
||||
restartBtn.removeAttribute('disabled');
|
||||
stopBtn.removeAttribute('disabled');
|
||||
} else {
|
||||
}
|
||||
if (command == 'stop_server') {
|
||||
startBtn.removeAttribute('disabled');
|
||||
restartBtn.setAttribute('disabled', 'disabled');
|
||||
stopBtn.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
{% end %}
|
||||
//<!-- this getCookie function is in base.html-->
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
|
||||
function get_server_log(){
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/ajax/server_log?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
dataType: 'text',
|
||||
success: function (data) {
|
||||
console.log('Got Log From Server')
|
||||
$('#virt_console').html(data);
|
||||
scrollConsole();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function new_line_handler(data) {
|
||||
$('#virt_console').append(data.line)
|
||||
const elem = document.getElementById('virt_console');
|
||||
const scrollDiff = (elem.scrollHeight - elem.scrollTop) - elem.clientHeight;
|
||||
if (!$("#stop_scroll").is(':checked') && scrollDiff < 450) {
|
||||
scrollConsole()
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
get_server_log()
|
||||
|
||||
webSocket.on('vterm_new_line', new_line_handler)
|
||||
});
|
||||
|
||||
$('#server_command').on('keydown', function (e) {
|
||||
if (e.which == 13){
|
||||
$(this).attr("disabled", "disabled"); //Disable textbox to prevent multiple submit
|
||||
send_command_to_server()
|
||||
$(this).removeAttr("disabled"); //Enable the textbox again if needed.
|
||||
$(this).focus();
|
||||
}
|
||||
else if (e.which == 38) {
|
||||
e.preventDefault();
|
||||
$('#server_command').val(cmdHistory.getPrev());
|
||||
} else if (e.which == 40) {
|
||||
e.preventDefault();
|
||||
$('#server_command').val(cmdHistory.getNext());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$("#submit").click(function(e) {
|
||||
e.preventDefault();
|
||||
send_command_to_server();
|
||||
|
||||
});
|
||||
|
||||
function scrollConsole(){
|
||||
var logview = $('#virt_console');
|
||||
if(logview.length)
|
||||
logview.scrollTop(logview[0].scrollHeight - logview.height());
|
||||
}
|
||||
|
||||
|
||||
function send_command_to_server(){
|
||||
var server_command = $("#server_command").val()
|
||||
console.log(server_command)
|
||||
|
||||
cmdHistory.push(server_command);
|
||||
|
||||
var token = getCookie("_xsrf")
|
||||
|
||||
data_to_send = { command :server_command, }
|
||||
|
||||
console.log('sending command: ' + server_command)
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/send_command?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: data_to_send,
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
$("#server_command").val('')
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const cmdHistory = {
|
||||
history: [],
|
||||
current: 0,
|
||||
push: function(cmd) {
|
||||
this.history.push(cmd);
|
||||
this.current = this.history.length - 1;
|
||||
},
|
||||
getPrev: function() {
|
||||
const prevCommand = this.history[this.current];
|
||||
this.current--;
|
||||
if (this.current < 0) this.current = 0;
|
||||
return prevCommand;
|
||||
},
|
||||
getNext: function() {
|
||||
this.current++;
|
||||
if (this.current > (this.history.length - 1)) {
|
||||
this.current = (this.history.length - 1);
|
||||
return '';
|
||||
}
|
||||
const nextCommand = this.history[this.current];
|
||||
return nextCommand;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/server/command?command=' + command + '&id=' + server_id,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (webSocket) {
|
||||
webSocket.on('update_button_status', function (updateButton) {
|
||||
if (updateButton.isUpdating) {
|
||||
if (updateButton.server_id == '{{ data['server_stats']['server_id']['server_id'] }}') {
|
||||
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>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (updateButton.server_id == '{{ data['server_stats']['server_id']['server_id'] }}') {
|
||||
window.location.reload()
|
||||
document.getElementById('update_control_buttons').innerHTML = '<button onclick="send_command(server_id, "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(server_id, "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
|
||||
// then to boolean via JSON.parse()
|
||||
let online = JSON.parse('{{ data['server_stats']['running'] }}'.toLowerCase());
|
||||
|
||||
let startBtn = document.querySelector('#start-btn');
|
||||
let restartBtn = document.querySelector('#restart-btn');
|
||||
let stopBtn = document.querySelector('#stop-btn');
|
||||
|
||||
{% if data['permissions']['Commands'] in data['user_permissions'] %}
|
||||
if (online) {
|
||||
startBtn.setAttribute('disabled', 'disabled');
|
||||
restartBtn.removeAttribute('disabled');
|
||||
stopBtn.removeAttribute('disabled');
|
||||
} else {
|
||||
startBtn.removeAttribute('disabled');
|
||||
restartBtn.setAttribute('disabled', 'disabled');
|
||||
stopBtn.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
{% end %}
|
||||
|
||||
let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
|
||||
function get_server_log() {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/ajax/server_log?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
dataType: 'text',
|
||||
success: function (data) {
|
||||
console.log('Got Log From Server')
|
||||
$('#virt_console').html(data);
|
||||
scrollConsole();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function new_line_handler(data) {
|
||||
$('#virt_console').append(data.line)
|
||||
const elem = document.getElementById('virt_console');
|
||||
const scrollDiff = (elem.scrollHeight - elem.scrollTop) - elem.clientHeight;
|
||||
if (!$("#stop_scroll").is(':checked') && scrollDiff < 450) {
|
||||
scrollConsole()
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
get_server_log()
|
||||
|
||||
webSocket.on('vterm_new_line', new_line_handler)
|
||||
});
|
||||
|
||||
$('#server_command').on('keydown', function (e) {
|
||||
if (e.which == 13) {
|
||||
$(this).attr("disabled", "disabled"); //Disable textbox to prevent multiple submit
|
||||
send_command_to_server()
|
||||
$(this).removeAttr("disabled"); //Enable the textbox again if needed.
|
||||
$(this).focus();
|
||||
}
|
||||
else if (e.which == 38) {
|
||||
e.preventDefault();
|
||||
$('#server_command').val(cmdHistory.getPrev());
|
||||
} else if (e.which == 40) {
|
||||
e.preventDefault();
|
||||
$('#server_command').val(cmdHistory.getNext());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$("#submit").click(function (e) {
|
||||
e.preventDefault();
|
||||
send_command_to_server();
|
||||
|
||||
});
|
||||
|
||||
function scrollConsole() {
|
||||
var logview = $('#virt_console');
|
||||
if (logview.length)
|
||||
logview.scrollTop(logview[0].scrollHeight - logview.height());
|
||||
}
|
||||
|
||||
|
||||
function send_command_to_server() {
|
||||
var server_command = $("#server_command").val()
|
||||
console.log(server_command)
|
||||
|
||||
cmdHistory.push(server_command);
|
||||
|
||||
var token = getCookie("_xsrf")
|
||||
|
||||
data_to_send = { command: server_command, }
|
||||
|
||||
console.log('sending command: ' + server_command)
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: '/ajax/send_command?id={{ data['server_stats']['server_id']['server_id'] }}',
|
||||
data: data_to_send,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
$("#server_command").val('')
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const cmdHistory = {
|
||||
history: [],
|
||||
current: 0,
|
||||
push: function (cmd) {
|
||||
this.history.push(cmd);
|
||||
this.current = this.history.length - 1;
|
||||
},
|
||||
getPrev: function () {
|
||||
const prevCommand = this.history[this.current];
|
||||
this.current--;
|
||||
if (this.current < 0) this.current = 0;
|
||||
return prevCommand;
|
||||
},
|
||||
getNext: function () {
|
||||
this.current++;
|
||||
if (this.current > (this.history.length - 1)) {
|
||||
this.current = (this.history.length - 1);
|
||||
return '';
|
||||
}
|
||||
const nextCommand = this.history[this.current];
|
||||
return nextCommand;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends ../public_base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<meta http-equiv="refresh" content="30">
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - {{ translate('dashboard', 'dashboard', data['lang']) }}{% end %}
|
||||
@ -24,39 +23,49 @@
|
||||
<tbody>
|
||||
{% for server in data['servers'] %}
|
||||
<tr>
|
||||
<td>
|
||||
<td id="server_name_{{ server['stats']['server_id']['server_id'] }}">
|
||||
<i class="fas fa-server"></i>
|
||||
{{ server['server_data']['server_name'] }}
|
||||
{{ server['server_data']['server_name'] }}
|
||||
</td>
|
||||
{% if server['stats']['int_ping_results'] != 'False' %}
|
||||
<td>
|
||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max', data['lang']) }}<br />
|
||||
</td>
|
||||
<td>
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
{% if server['raw_ping_result']['icon'] %}
|
||||
<img src="data:image/png;base64,{% raw server['raw_ping_result']['icon'] %}" alt="icon"/>
|
||||
{% else %}
|
||||
<img src="/static/assets/images/pack.png" alt="icon" />
|
||||
{% end %}
|
||||
<span id="input_motd_{{ server['stats']['server_id']['server_id'] }}" class="input_motd">{{ server['stats']['desc'] }}</span> <br />
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
{% if server['stats']['int_ping_results'] != 'False' %}
|
||||
<td id="server_players_{{ server['stats']['server_id']['server_id'] }}">
|
||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max',
|
||||
data['lang']) }}<br />
|
||||
</td>
|
||||
<td id="server_motd_{{ server['stats']['server_id']['server_id'] }}">
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
{% if server['raw_ping_result']['icon'] %}
|
||||
<img src="data:image/png;base64,{% raw server['raw_ping_result']['icon'] %}" alt="icon" />
|
||||
{% else %}
|
||||
<td colspan="3">
|
||||
<span class="text-warning"><i class="fas fa-exclamation-triangle"></i> Crafty can't get infos from this Server </span>
|
||||
</td>
|
||||
<img src="/static/assets/images/pack.png" alt="icon" />
|
||||
{% end %}
|
||||
<td>
|
||||
<span id="input_motd_{{ server['stats']['server_id']['server_id'] }}" class="input_motd">{{
|
||||
server['stats']['desc'] }}</span> <br />
|
||||
{% end %}
|
||||
</td>
|
||||
<td id="server_version_{{ server['stats']['server_id']['server_id'] }}">
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td id="server_players_{{ server['stats']['server_id']['server_id'] }}">
|
||||
<span class="text-warning"><i class="fas fa-exclamation-triangle"></i></span>
|
||||
</td>
|
||||
<td id="server_motd_{{ server['stats']['server_id']['server_id'] }}">
|
||||
<span class="text-warning">Crafty can't get infos from this Server </span>
|
||||
</td>
|
||||
<td id="server_version_{{ server['stats']['server_id']['server_id'] }}">
|
||||
<span class="text-warning"><i class="fas fa-question"></i></i></span>
|
||||
</td>
|
||||
{% end %}
|
||||
<td id="server_online_status_{{ server['stats']['server_id']['server_id'] }}">
|
||||
{% if server['stats']['running'] %}
|
||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang']) }}</span>
|
||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])
|
||||
}}</span>
|
||||
{% else %}
|
||||
<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang']) }}</span>
|
||||
<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])
|
||||
}}</span>
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -72,14 +81,83 @@
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script src="/static/assets/js/motd.js"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var all_motds = Array.from(document.getElementsByClassName('input_motd'));
|
||||
for (element of all_motds) {
|
||||
initParser(element.id, element.id);
|
||||
};
|
||||
}());
|
||||
</script>
|
||||
<script src="/static/assets/js/motd.js"></script>
|
||||
<script>
|
||||
function display_motd() {
|
||||
var all_motds = Array.from(document.getElementsByClassName('input_motd'));
|
||||
for (element of all_motds) {
|
||||
initParser(element.id, element.id);
|
||||
};
|
||||
}
|
||||
|
||||
function update_one_server_status(server) {
|
||||
server_players = document.getElementById('server_players_' + server.id);
|
||||
server_motd = document.getElementById('server_motd_' + server.id);
|
||||
server_version = document.getElementById('server_version_' + server.id);
|
||||
server_online_status = document.getElementById('server_online_status_' + server.id);
|
||||
|
||||
/* TODO Update each element */
|
||||
if (server.int_ping_results) {
|
||||
|
||||
/* Update Players */
|
||||
if (server.players)
|
||||
{
|
||||
server_players.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />`
|
||||
}
|
||||
|
||||
/* Update Motd */
|
||||
var motd = "";
|
||||
if (server.desc) {
|
||||
if (server.icon) {
|
||||
motd = `<img src="data:image/png;base64,` + server.icon + `" alt="icon" /> `;
|
||||
}
|
||||
else {
|
||||
motd = `<img src="/static/assets/images/pack.png" alt="icon" /> `;
|
||||
}
|
||||
|
||||
motd = motd + `<span id="input_motd_` + server.id + `" class="input_motd">` + server.desc + `</span> <br />`;
|
||||
server_motd.innerHTML = motd;
|
||||
}
|
||||
|
||||
/* Version */
|
||||
if (server.version)
|
||||
{
|
||||
server_version.innerHTML = server.version
|
||||
}
|
||||
}
|
||||
else {
|
||||
server_players.innerHTML = `<span class="text-warning"><i class="fas fa-exclamation-triangle"></i></span>`;
|
||||
server_motd.innerHTML = `<span class="text-warning">Crafty can't get infos from this Server </span>`;
|
||||
server_version.innerHTML = `<span class="text-warning"><i class="fas fa-question"></i></i></span>`
|
||||
}
|
||||
|
||||
/* Update Online Status */
|
||||
var online_status = "";
|
||||
if (server.running) {
|
||||
online_status = `<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])}}</span>`;
|
||||
}
|
||||
else {
|
||||
online_status = `<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])}}</span>`;
|
||||
}
|
||||
server_online_status.innerHTML = online_status;
|
||||
}
|
||||
|
||||
function update_servers_status(data) {
|
||||
for (server of data) {
|
||||
update_one_server_status(server);
|
||||
}
|
||||
display_motd();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
display_motd()
|
||||
|
||||
if (webSocket)
|
||||
{
|
||||
webSocket.on('update_server_status', update_servers_status);
|
||||
}
|
||||
}());
|
||||
</script>
|
||||
|
||||
{% end %}
|
@ -1,60 +1,118 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
{% block meta %}{% end %}
|
||||
<title>{% block title %}{{ _('Default') }}{% end %}</title>
|
||||
<!-- plugins:css -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
|
||||
<!-- endinject -->
|
||||
<!-- Plugin css for this page -->
|
||||
<!-- End Plugin css for this page -->
|
||||
<!-- Layout styles -->
|
||||
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
||||
<!-- End Layout styles -->
|
||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
||||
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="container-scroller">
|
||||
<div class="container-fluid page-body-wrapper full-page-wrapper">
|
||||
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||
<div class="row w-100">
|
||||
<div class="mx-auto">
|
||||
<div class="auto-form-wrapper">
|
||||
{% block content %}
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
{% block meta %}{% end %}
|
||||
<title>{% block title %}{{ _('Default') }}{% end %}</title>
|
||||
<!-- plugins:css -->
|
||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
||||
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
|
||||
<!-- endinject -->
|
||||
<!-- Plugin css for this page -->
|
||||
<!-- End Plugin css for this page -->
|
||||
<!-- Layout styles -->
|
||||
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
||||
<!-- End Layout styles -->
|
||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
||||
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
||||
</head>
|
||||
|
||||
<body class="dark-theme">
|
||||
<div class="container-scroller">
|
||||
<div class="container-fluid page-body-wrapper full-page-wrapper">
|
||||
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||
<div class="row w-100">
|
||||
<div class="mx-auto">
|
||||
<div class="auto-form-wrapper">
|
||||
{% block content %}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
</div>
|
||||
<!-- page-body-wrapper ends -->
|
||||
<!-- content-wrapper ends -->
|
||||
</div>
|
||||
<!-- container-scroller -->
|
||||
<!-- plugins:js -->
|
||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||
<!-- endinject -->
|
||||
<!-- inject:js -->
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
<script src="/static/assets/js/shared/misc.js"></script>
|
||||
<script src="/static/assets/js/shared/settings.js"></script>
|
||||
<script src="/static/assets/js/shared/todolist.js"></script>
|
||||
<!-- endinject -->
|
||||
|
||||
{% block js %}
|
||||
<!-- Custom js for this page -->
|
||||
<!-- End custom js for this page -->
|
||||
{% end %}
|
||||
|
||||
</body>
|
||||
<!-- page-body-wrapper ends -->
|
||||
</div>
|
||||
<!-- container-scroller -->
|
||||
<!-- plugins:js -->
|
||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||
<!-- endinject -->
|
||||
<!-- inject:js -->
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
<script src="/static/assets/js/shared/misc.js"></script>
|
||||
<script src="/static/assets/js/shared/settings.js"></script>
|
||||
<script src="/static/assets/js/shared/todolist.js"></script>
|
||||
<!-- endinject -->
|
||||
<script>
|
||||
|
||||
// {% if request.protocol == 'https' %}
|
||||
let usingWebSockets = true;
|
||||
|
||||
let listenEvents = [];
|
||||
|
||||
try {
|
||||
pageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
|
||||
page = 'page=' + encodeURIComponent(location.pathname)
|
||||
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + page + '&' + pageQueryParams);
|
||||
wsInternal.onopen = function () {
|
||||
console.log('opened WebSocket connection:', wsInternal)
|
||||
};
|
||||
wsInternal.onmessage = function (rawMessage) {
|
||||
var message = JSON.parse(rawMessage.data);
|
||||
|
||||
console.log('got message: ', message)
|
||||
|
||||
listenEvents
|
||||
.filter(listenedEvent => listenedEvent.event == message.event)
|
||||
.forEach(listenedEvent => listenedEvent.callback(message.data))
|
||||
};
|
||||
wsInternal.onerror = function (errorEvent) {
|
||||
console.error('WebSocket Error', errorEvent);
|
||||
};
|
||||
wsInternal.onclose = function (closeEvent) {
|
||||
console.log('Closed WebSocket', closeEvent);
|
||||
};
|
||||
|
||||
|
||||
webSocket = {
|
||||
on: function (event, callback) {
|
||||
console.log('registered ' + event + ' event');
|
||||
listenEvents.push({ event: event, callback: callback })
|
||||
},
|
||||
emit: function (event, data) {
|
||||
var message = {
|
||||
event: event,
|
||||
data: data
|
||||
}
|
||||
|
||||
wsInternal.send(JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while making websocket helpers', error);
|
||||
usingWebSockets = false;
|
||||
}
|
||||
// {% else %}
|
||||
let usingWebSockets = false;
|
||||
warn('WebSockets are not supported in Crafty if not using the https protocol')
|
||||
var webSocket;
|
||||
// {% end%}
|
||||
|
||||
</script>
|
||||
{% block js %}
|
||||
<!-- Custom js for this page -->
|
||||
<!-- End custom js for this page -->
|
||||
{% end %}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
12
app/migrations/20211120221511_api_keys.py
Normal file
12
app/migrations/20211120221511_api_keys.py
Normal file
@ -0,0 +1,12 @@
|
||||
import peewee
|
||||
import datetime
|
||||
|
||||
|
||||
def migrate(migrator, database, **kwargs):
|
||||
migrator.add_columns('users', valid_tokens_from=peewee.DateTimeField(default=datetime.datetime.now))
|
||||
migrator.drop_columns('users', ['api_token'])
|
||||
|
||||
|
||||
def rollback(migrator, database, **kwargs):
|
||||
migrator.drop_columns('users', ['valid_tokens_from'])
|
||||
migrator.add_columns('users', api_token=peewee.CharField(default="", unique=True, index=True))
|
23
app/migrations/20211121233959_multi_api_keys.py
Normal file
23
app/migrations/20211121233959_multi_api_keys.py
Normal file
@ -0,0 +1,23 @@
|
||||
import peewee
|
||||
import datetime
|
||||
from app.classes.models.users import Users
|
||||
|
||||
|
||||
def migrate(migrator, db):
|
||||
class ApiKeys(peewee.Model):
|
||||
token_id = peewee.AutoField()
|
||||
name = peewee.CharField(default='', unique=True, index=True)
|
||||
created = peewee.DateTimeField(default=datetime.datetime.now)
|
||||
user = peewee.ForeignKeyField(Users, backref='api_token', index=True)
|
||||
server_permissions = peewee.CharField(default='00000000')
|
||||
crafty_permissions = peewee.CharField(default='000')
|
||||
superuser = peewee.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
table_name = 'api_keys'
|
||||
|
||||
migrator.create_table(ApiKeys)
|
||||
|
||||
|
||||
def rollback(migrator, db):
|
||||
migrator.drop_table('api_keys')
|
@ -122,6 +122,7 @@
|
||||
"serverStats": {
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"starting": "Verzögerter Start",
|
||||
"serverStatus": "Server Status",
|
||||
"serverStarted": "Server gestartet",
|
||||
"serverUptime": "Server-Betriebszeit",
|
||||
@ -342,5 +343,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Warnung: </strong>Crafty funktioniert nicht richtig, wenn JavaScript nicht aktiviert ist!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Diesen API Schlüssel entfernen? Dies kann nicht rückgängig gemacht werden.",
|
||||
"deleteKeyConfirmationTitle": "Folgenden API Schlüssel entfernen ${keyId}?"
|
||||
}
|
||||
}
|
@ -76,10 +76,6 @@
|
||||
"clickRoot": "Click here to select Root Dir",
|
||||
"explainRoot": "Please click the button below to select your server's root dir inside of the archive"
|
||||
},
|
||||
"usersConfig":{
|
||||
"deleteUser": "Delete user: ",
|
||||
"confirmDelete": "Are you sure you want to delete this user? This action is irreversible."
|
||||
},
|
||||
"dashboard": {
|
||||
"dashboard": "Dashboard",
|
||||
"memUsage": "Memory Usage",
|
||||
@ -133,6 +129,7 @@
|
||||
"serverStats": {
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"starting": "Delayed-Start",
|
||||
"serverStatus": "Server Status",
|
||||
"serverStarted": "Server Started",
|
||||
"serverUptime": "Server Uptime",
|
||||
@ -173,14 +170,21 @@
|
||||
"loadingBannedPlayers": "Loading Banned Players"
|
||||
},
|
||||
"serverSchedules":{
|
||||
"areYouSure": "Deleted Scheduled Task?",
|
||||
"areYouSure": "Delete Scheduled Task?",
|
||||
"confirmDelete": "Do you want to delete this scheduled task? This cannot be undone.",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"cannotSee": "Not seeing everything?",
|
||||
"cannotSeeOnMobile": "Try clicking on a scheduled task for full details."
|
||||
},
|
||||
|
||||
"notify":{
|
||||
"supportLogs": "Support Logs",
|
||||
"activityLog": "Activity Logs",
|
||||
"logout": "Logout",
|
||||
"preparingLogs": " Please wait while we prepare your logs... We`ll send a notification when they`re ready. This may take a while for large deployments.",
|
||||
"downloadLogs": "Download Support Logs?",
|
||||
"finishedPreparing": "We've finished preparing your support logs. Please click download to download"
|
||||
},
|
||||
"serverBackups": {
|
||||
"backupNow": "Backup Now!",
|
||||
"backupAtMidnight": "Auto-backup at midnight?",
|
||||
@ -297,12 +301,84 @@
|
||||
]
|
||||
},
|
||||
"panelConfig": {
|
||||
"pageTitle": "Panel Config",
|
||||
"users": "Users",
|
||||
"roles": "Roles",
|
||||
"newUser": "Add New User",
|
||||
"newRole": "Add New Role",
|
||||
"user": "User",
|
||||
"enabled": "Enabled",
|
||||
"allowedServers": "Allowed Servers",
|
||||
"assignedRoles": "Assigned Roles",
|
||||
"edit": "Edit",
|
||||
"role": "Role",
|
||||
"roleUsers": "Role Users",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"superConfirmTitle": "Enable Super User? Are you sure?",
|
||||
"superConfirm": "Proceed only if you want this user to have access to EVERYTHING (all user accounts, servers, panel configs, etc). They can even remove your super user access."
|
||||
|
||||
"superConfirmTitle": "Enable superuser? Are you sure?",
|
||||
"superConfirm": "Proceed only if you want this user to have access to EVERYTHING (all user accounts, servers, panel settings, etc.). They can even revoke your superuser rights."
|
||||
},
|
||||
"userConfig": {
|
||||
"pageTitle": "Edit User",
|
||||
"pageTitleNew": "Create User",
|
||||
"config": "Config",
|
||||
"apiKey": "API Keys",
|
||||
"userSettings": "User Settings",
|
||||
"userName": "User Name",
|
||||
"userNameDesc": "What do you want to call this user?",
|
||||
"password": "Password",
|
||||
"repeat": "Repeat Password",
|
||||
"leaveBlank": "To edit user without changing password leave it blank.",
|
||||
"craftyPermDesc": "Crafty permissions this user has ",
|
||||
"gravEmail": "Gravatar™ Email",
|
||||
"gravDesc": "This email is strictly for use with Gravatar™. Crafty will not, under any circumstance make use of this email for anything other than looking up your Gravatar™",
|
||||
"userLang": "User Language",
|
||||
"userRoles": "User Roles",
|
||||
"userRolesDesc": "Roles this user is a member of.",
|
||||
"roleName": "Role Name",
|
||||
"member": "Member?",
|
||||
"craftyPerms": "Crafty Permissons: ",
|
||||
"permName":"Permission Name",
|
||||
"auth": "Authorized? ",
|
||||
"uses": "Number of uses allowed (-1==No Limit)",
|
||||
"super": "Super User",
|
||||
"enabled": "Enabled",
|
||||
"configArea": "User Config Area",
|
||||
"configAreaDesc": "Here is where you change all of your user settings",
|
||||
"created": "Created: ",
|
||||
"lastLogin": "Last Login: ",
|
||||
"lastUpdate": "Last Update: ",
|
||||
"lastIP": "Last IP: ",
|
||||
"deleteUserB": "Delete User",
|
||||
"notExist": "You cannot delete something that doesn't exist!",
|
||||
"delSuper": "You cannot delete a super user",
|
||||
"deleteUser": "Delete user: ",
|
||||
"confirmDelete": "Are you sure you want to delete this user? This action is irreversible."
|
||||
},
|
||||
"rolesConfig": {
|
||||
"pageTitle": "Edit Role",
|
||||
"pageTitleNew": "New Role",
|
||||
"config": "Role Config",
|
||||
"roleTitle": "Roles Settings",
|
||||
"roleName": "Role Name: ",
|
||||
"roleDesc": "What would you like to call this role?",
|
||||
"roleServers": "Allowed Servers",
|
||||
"serversDesc": "servers this role is allowed to access",
|
||||
"serverName": "Server Name",
|
||||
"serverAccess": "Access?",
|
||||
"rolePerms": "Role Permissions",
|
||||
"permsServer": "Permissions this role has for these specified servers",
|
||||
"permName": "Permission Name",
|
||||
"permAccess": "Access?",
|
||||
"roleUsers": "Role Users: ",
|
||||
"roleUserName": "User Name",
|
||||
"roleConfigArea": "Role Config Area",
|
||||
"configDesc": "Here is where you can change the configuration of your role",
|
||||
"created": "Created: ",
|
||||
"configUpdate": "Last Updated: ",
|
||||
"delRole": "Delete Role",
|
||||
"doesNotExist": "You cannot delete something that does not exist yet"
|
||||
},
|
||||
"datatables": {
|
||||
"i18n": {
|
||||
@ -370,5 +446,27 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"pageTitle": "Edit User API Keys",
|
||||
"config": "Config",
|
||||
"apiKeys": "API Keys",
|
||||
"name": "Name",
|
||||
"created": "Created",
|
||||
"perms": "Permissions",
|
||||
"buttons": "Buttons",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"server": "Server: ",
|
||||
"crafty": "Crafty: ",
|
||||
"getToken": "Get A Token",
|
||||
"createNew": "Create new API Token",
|
||||
"nameDesc": "What would you like to call this API token? ",
|
||||
"permName": "Permission Name",
|
||||
"auth":"Authorized? ",
|
||||
"superUser": "Super User",
|
||||
"deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.",
|
||||
"deleteKeyConfirmationTitle": "Remove API key ${keyId}?"
|
||||
}
|
||||
|
||||
}
|
@ -123,6 +123,7 @@
|
||||
"serverStats": {
|
||||
"online": "En línea",
|
||||
"offline": "Desconectado",
|
||||
"starting": "Inicio-retrasado",
|
||||
"serverStatus": "Estado del Servidor",
|
||||
"serverStarted": "Servidor Iniciado",
|
||||
"serverUptime": "Actividad del Servidor",
|
||||
@ -346,5 +347,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Aviso: </strong>¡Crafty no funciona correctamente cuando JavaScript no está habilitado!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "¿Quieres eliminar esta clave de API? Esto no se puede deshacer.",
|
||||
"deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?"
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
"embarassing": "No, tämähän on noloa.",
|
||||
"error": "Virhe!",
|
||||
"start-error": "Palvelin {} ei käynnistynyt virhekoodilla: {}",
|
||||
"closedPort": "Olemme havainneet, että portti {} ei ehkä ole auki isäntäverkossa tai palomuuri estää sen. Etäasiakkaan yhteydet palvelimeen voivat olla rajallisia.",
|
||||
"portReminder": "Olemme havainneet, että tämä on ensimmäinen kerta, kun {} on käynnistetty. Varmista, että välität porttia {} reitittimesi/palomuurisi kautta, jotta se on käytössä internetistä.",
|
||||
"internet": "Olemme havainneet, että Crafty -koneella ei ole Internet -yhteyttä. Asiakasyhteydet palvelimelle voivat olla rajalliset.",
|
||||
"eulaTitle": "Hyväksy EULA",
|
||||
"eulaMsg": "Sinun on hyväksyttävä EULA. Kopio Mojang EULA:sta on linkitetty tämän viestin alla.",
|
||||
@ -68,8 +68,17 @@
|
||||
"downloading": "Lataamme palvelinta...",
|
||||
"addRole": "Lisää Palvelin Olemassa Oleviin Rooleihin",
|
||||
"autoCreate": "Jos ketään ei valita, Crafty tekee sellaisen!",
|
||||
"selectRole": "Valitse roolit"
|
||||
|
||||
"selectRole": "Valitse roolit",
|
||||
"selectZipDir": "Valitse arkistosta hakemisto, josta haluat meidän purkavan tiedostot",
|
||||
"close": "Sulje",
|
||||
"save": "Tallenna",
|
||||
"selectRoot": "Valitse arkiston päähakemisto",
|
||||
"clickRoot": "Napsauta tästä valitaksesi juurihakemiston",
|
||||
"explainRoot": "Napsauta alla olevaa painiketta valitaksesi palvelimesi juurihakemiston arkistosta"
|
||||
},
|
||||
"usersConfig":{
|
||||
"deleteUser": "Poista käyttäjä: ",
|
||||
"confirmDelete": "Oletko varma, että haluat poistaa tämän käyttäjän? Tätä ei voi peruuttaa."
|
||||
},
|
||||
"dashboard": {
|
||||
"dashboard": "Kojelauta",
|
||||
@ -124,6 +133,7 @@
|
||||
"serverStats": {
|
||||
"online": "Päällä",
|
||||
"offline": "Pois päältä",
|
||||
"starting": "Myöhästynyt lähtö",
|
||||
"serverStatus": "Palvelimen tila",
|
||||
"serverStarted": "Palvelin käynnistyi",
|
||||
"serverUptime": "Palvelimen käyttöaika",
|
||||
@ -134,7 +144,8 @@
|
||||
"description": "Kuvaus",
|
||||
"errorCalculatingUptime": "Virhe laskettaessa käyttöaikaa",
|
||||
"serverTime": "UTC aikaa",
|
||||
"unableToConnect": "Yhteyden muodostaminen epäonnistui"
|
||||
"unableToConnect": "Yhteyden muodostaminen epäonnistui",
|
||||
"serverTimeZone": "Palvelimen aikavyöhyke"
|
||||
},
|
||||
"serverDetails": {
|
||||
"serverDetails": "Palvelimen tiedot",
|
||||
@ -162,6 +173,14 @@
|
||||
"bannedPlayers": "Kielletyt pelaajat",
|
||||
"loadingBannedPlayers": "Ladataan kiellettyjen pelaajien listaa"
|
||||
},
|
||||
"serverSchedules":{
|
||||
"areYouSure": "Poista ajoitettu tehtävä?",
|
||||
"confirmDelete": "Haluatko poistaa tämän ajoitetun tehtävän? Tätä ei voi peruuttaa.",
|
||||
"cancel": "Peruuta",
|
||||
"confirm": "Vahvista",
|
||||
"cannotSee": "Etkö näe kaikkea?",
|
||||
"cannotSeeOnMobile": "Napsauta ajoitettua tehtävää saadaksesi täydet tiedot."
|
||||
},
|
||||
"serverBackups": {
|
||||
"backupNow": "Varmuuskopioi nyt!",
|
||||
"backupAtMidnight": "Automaattisesti varmuuskopioi keskiyöllä?",
|
||||
@ -206,14 +225,15 @@
|
||||
"unsupportedLanguage": "Varoitus: Tätä tiedostotyyppiä ei tueta",
|
||||
"keybindings": "Pikanäppäimet",
|
||||
"fileReadError": "Tiedoston lukuvirhe",
|
||||
"upload": "Lataa",
|
||||
"upload": "Lähetä",
|
||||
"unzip": "Pura",
|
||||
"clickUpload": "Valitse tiedostosi napsauttamalla tätä",
|
||||
"uploadTitle": "Lähetä tiedostot: ",
|
||||
"waitUpload": "Odota, kunnes lataamme tiedostosi ... Tämä voi kestää hetken.",
|
||||
"stayHere": "ÄLÄ JÄTÄ SIVUTA!",
|
||||
"close": "Kiinni",
|
||||
"download": "Ladata"
|
||||
"waitUpload": "Odota, kun lähetämme tiedostojasi... Tämä voi kestää hetken.",
|
||||
"stayHere": "ÄLÄ POISTU SIVULTA!",
|
||||
"close": "Sulje",
|
||||
"download": "Lataa",
|
||||
"loadingRecords": "Ladataan tiedostoja..."
|
||||
},
|
||||
"serverConfig": {
|
||||
"serverName": "Palvelimen nimi",
|
||||
@ -248,12 +268,12 @@
|
||||
"bePatientUpdate": "Ole kärsivällinen, kun päivitämme palvelinta. Latausajat voivat vaihdella Internet-nopeutesi mukaan.<br /> Tämä näyttö päivittyy hetkessä",
|
||||
"sendingRequest": "Pyyntöäsi lähetetään...",
|
||||
"deleteServerQuestion": "Poistetaanko palvelin?",
|
||||
"deleteServerQuestionMessage": "Haluatko varmasti poistaa tämän palvelimen? Tämän jälkeen ei ole paluuta...",
|
||||
"deleteServerQuestionMessage": "Haluatko varmasti poistaa tämän palvelimen? Tätä ei voi peruuttaa...",
|
||||
"yesDelete": "Kyllä, poista",
|
||||
"noDelete": "Ei, mene takaisin",
|
||||
"deleteFilesQuestion": "Poistetaanko palvelintiedostot koneelta?",
|
||||
"deleteFilesQuestionMessage": "Haluatko Craftyn poistavan kaikki palvelintiedostot isäntäkoneelta? <br><br><strong> Tämä sisältää palvelimen varmuuskopiot. <strong>",
|
||||
"yesDeleteFiles": "Kyllä, poista tiedostoja",
|
||||
"yesDeleteFiles": "Kyllä, poista tiedostot",
|
||||
"noDeleteFiles": "Ei, poista vain paneelista",
|
||||
"sendingDelete": "Poistetaan palvelinta",
|
||||
"bePatientDelete": "Ole kärsivällinen, kun poistamme palvelimesi Crafty-paneelista. Tämä näyttö sulkeutuu hetken kuluttua.",
|
||||
@ -279,7 +299,9 @@
|
||||
"panelConfig": {
|
||||
"save": "Tallenna",
|
||||
"cancel": "Peruuta",
|
||||
"delete": "Poista"
|
||||
"delete": "Poista",
|
||||
"superConfirmTitle": "Otetaanko järjestelmänvalvojan oikeudet käyttöön? Oletko varma?",
|
||||
"superConfirm": "Jatka vain, jos haluat, että tällä käyttäjällä on pääsy KAIKKEEN (kaikki käyttäjätilit, palvelimet, paneelin asetukset jne.). Hän voi jopa poistaa sinun järjestelmänvalvojan oikeutesi."
|
||||
},
|
||||
"datatables": {
|
||||
"i18n": {
|
||||
@ -371,5 +393,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Varoitus: </strong>Crafty ei toimi kunnolla ilman JavaScriptiä!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Haluatko varmasti poistaa tämän API-avaimen? Tämä on peruuttamaton toimenpide!",
|
||||
"deleteKeyConfirmationTitle": "Poistetaanko API-avain ${keyId}?"
|
||||
}
|
||||
}
|
@ -123,6 +123,7 @@
|
||||
"serverStats": {
|
||||
"online": "En Ligne",
|
||||
"offline": "Hors Ligne",
|
||||
"starting": "Démarrage en cours",
|
||||
"serverStatus": "Statut du Serveur",
|
||||
"serverStarted": "Serveur Démarré",
|
||||
"serverUptime": "Serveur Disponible depuis",
|
||||
@ -346,5 +347,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Attention: </strong>Crafty ne fonctionne pas correctement si JavaScript n'est pas activé !"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Es-tu sûr de vouloir supprimer cette clé API? Tu ne pourras plus revenir en arrière.",
|
||||
"deleteKeyConfirmationTitle": "Supprimer la clé API ${keyId}?"
|
||||
}
|
||||
}
|
@ -122,6 +122,7 @@
|
||||
"serverStats": {
|
||||
"online": "Na mreži",
|
||||
"offline": "Izvan mreže",
|
||||
"starting": "Odgođeno pokretanje",
|
||||
"serverStatus": "Status poslužitelja",
|
||||
"serverStarted": "Poslužitelj pokrenut",
|
||||
"serverUptime": "Vrijeme rada poslužitelja",
|
||||
@ -342,5 +343,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Upozorenje: </strong>Crafty ne radi ispravno kada JavaScript nije omogućen!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Želite li izbrisati ovaj API ključ? Ova radnja je nepovratna.",
|
||||
"deleteKeyConfirmationTitle": "Ukloniti API ključ ${keyId}?"
|
||||
}
|
||||
}
|
@ -122,6 +122,7 @@
|
||||
"serverStats": {
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"starting": "Avvio ritardato",
|
||||
"serverStatus": "Stato del server",
|
||||
"serverStarted": "Server Avviato",
|
||||
"serverUptime": "Tempo di esecuzione",
|
||||
|
@ -123,6 +123,7 @@
|
||||
"serverStats": {
|
||||
"online": "Turnd on",
|
||||
"offline": "Turnd off",
|
||||
"starting": "I waitz b4 I start",
|
||||
"serverStatus": "Servr Status",
|
||||
"serverStarted": "Servr Started",
|
||||
"serverUptime": "How longz servr been awaek",
|
||||
|
@ -122,6 +122,7 @@
|
||||
"serverStats": {
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"starting": "Vertraagde start",
|
||||
"serverStatus": "Server Status",
|
||||
"serverStarted": "Server gestart",
|
||||
"serverUptime": "Server Uptime",
|
||||
|
@ -123,6 +123,7 @@
|
||||
"serverStats": {
|
||||
"online": "运行中",
|
||||
"offline": "已停止",
|
||||
"starting": "延迟启动",
|
||||
"serverStatus": "服务器状态",
|
||||
"serverStarted": "服务器已启动",
|
||||
"serverUptime": "服务器正常运行时间",
|
||||
@ -346,5 +347,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>警告:</strong>Crafty 无法在没有 JavaScript 的情况下使用!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "您确定要删除该 API 密钥吗?此操作无法撤销。",
|
||||
"deleteKeyConfirmationTitle": "删除 API 密钥 ${keyId}?"
|
||||
}
|
||||
}
|
@ -6,13 +6,20 @@ upstream crafty {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen 80;
|
||||
server_name <DOMAIN>;
|
||||
if ($host !~* ^<SUBDOMAIN>\.<EXAMPLE>\.com$ ) {
|
||||
return 444;
|
||||
}
|
||||
rewrite ^(.*) https://$host$1 permanent;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name <DOMAIN>;
|
||||
if ($host !~* ^<SUBDOMAIN>\.<EXAMPLE>\.com$ ) {
|
||||
return 444;
|
||||
}
|
||||
ssl_certificate <CERIFICATE_LOCATION>;
|
||||
ssl_certificate_key <KEYFILE_LOCATION>;
|
||||
location / {
|
||||
|
@ -1,18 +1,19 @@
|
||||
cryptography~=3.4
|
||||
argon2-cffi~=20.1
|
||||
bleach~=3.1
|
||||
colorama~=0.4
|
||||
cryptography~=3.4
|
||||
libgravatar~=1.0.0
|
||||
peewee~=3.13
|
||||
pexpect~=4.8
|
||||
psutil~=5.7
|
||||
pyOpenSSL~=19.1.0
|
||||
cryptography==3.4
|
||||
argon2-cffi==20.1
|
||||
bleach==3.1
|
||||
colorama==0.4
|
||||
cryptography==3.4
|
||||
libgravatar==1.0.0
|
||||
peewee==3.13
|
||||
pexpect==4.8
|
||||
psutil==5.7
|
||||
pyOpenSSL==19.1.0
|
||||
PyYAML==5.3.1
|
||||
requests~=2.26
|
||||
termcolor~=1.1
|
||||
tornado~=6.0
|
||||
requests==2.26
|
||||
termcolor==1.1
|
||||
tornado==6.0
|
||||
cached_property==1.5.2
|
||||
apscheduler~=3.8.1
|
||||
cron-validator~=1.0.3
|
||||
tzlocal~=4.1
|
||||
apscheduler==3.8.1
|
||||
cron-validator==1.0.3
|
||||
tzlocal==4.0
|
||||
pyjwt==2.3
|
||||
|
Loading…
Reference in New Issue
Block a user