mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'lukas-bugfixes' into 'dev'
Bugfixes 🐛❌ and more See merge request crafty-controller/crafty-commander!30
This commit is contained in:
commit
9dfc6ed449
@ -17,8 +17,8 @@ try:
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ try:
|
|||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -29,10 +29,6 @@ class MainPrompt(cmd.Cmd, object):
|
|||||||
# overrides the default Prompt
|
# overrides the default Prompt
|
||||||
prompt = "Crafty Controller v{} > ".format(helper.get_version_string())
|
prompt = "Crafty Controller v{} > ".format(helper.get_version_string())
|
||||||
|
|
||||||
def __init__(self, tasks_manager):
|
|
||||||
super().__init__()
|
|
||||||
self.tasks_manager = tasks_manager
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def emptyline():
|
def emptyline():
|
||||||
pass
|
pass
|
||||||
@ -49,6 +45,9 @@ class MainPrompt(cmd.Cmd, object):
|
|||||||
console.critical("Unable to write exit file due to error: {}".format(e))
|
console.critical("Unable to write exit file due to error: {}".format(e))
|
||||||
|
|
||||||
def do_exit(self, line):
|
def do_exit(self, line):
|
||||||
|
self.universal_exit()
|
||||||
|
|
||||||
|
def universal_exit(self):
|
||||||
logger.info("Stopping all server daemons / threads")
|
logger.info("Stopping all server daemons / threads")
|
||||||
console.info("Stopping all server daemons / threads - This may take a few seconds")
|
console.info("Stopping all server daemons / threads - This may take a few seconds")
|
||||||
websocket_helper.disconnect_all()
|
websocket_helper.disconnect_all()
|
||||||
|
@ -9,8 +9,8 @@ try:
|
|||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logging.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
print("Import Error: Unable to load {} module".format(e, e.name))
|
print("Import Error: Unable to load {} module".format(e.name))
|
||||||
from app.classes.shared.installer import installer
|
from app.classes.shared.installer import installer
|
||||||
installer.do_install()
|
installer.do_install()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -28,7 +28,7 @@ try:
|
|||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
class Helpers:
|
class Helpers:
|
||||||
|
@ -18,8 +18,8 @@ try:
|
|||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
schema_version = (0, 1, 0) # major, minor, patch semver
|
schema_version = (0, 1, 0) # major, minor, patch semver
|
||||||
@ -503,6 +503,43 @@ class db_shortcuts:
|
|||||||
except DoesNotExist:
|
except DoesNotExist:
|
||||||
return None
|
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 = db_shortcuts.add_user_roles(user)
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_user_roles(user):
|
||||||
|
if type(user) == dict:
|
||||||
|
user_id = user['user_id']
|
||||||
|
else:
|
||||||
|
user_id = user.user_id
|
||||||
|
|
||||||
|
# I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas
|
||||||
|
|
||||||
|
roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id)
|
||||||
|
# TODO: this query needs to be narrower
|
||||||
|
roles = set()
|
||||||
|
for r in roles_query:
|
||||||
|
roles.add(r.role_id.role_id)
|
||||||
|
#servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id)
|
||||||
|
## TODO: this query needs to be narrower
|
||||||
|
servers = set()
|
||||||
|
#for s in servers_query:
|
||||||
|
# servers.add(s.server_id.server_id)
|
||||||
|
user['roles'] = roles
|
||||||
|
#user['servers'] = servers
|
||||||
|
#logger.debug("user: ({}) {}".format(user_id, user))
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user(user_id):
|
def get_user(user_id):
|
||||||
if user_id == 0:
|
if user_id == 0:
|
||||||
@ -523,19 +560,8 @@ class db_shortcuts:
|
|||||||
user = model_to_dict(Users.get(Users.user_id == user_id))
|
user = model_to_dict(Users.get(Users.user_id == user_id))
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id)
|
# I know it should apply it without setting it but I'm just making sure
|
||||||
# TODO: this query needs to be narrower
|
user = db_shortcuts.add_user_roles(user)
|
||||||
roles = set()
|
|
||||||
for r in roles_query:
|
|
||||||
roles.add(r.role_id.role_id)
|
|
||||||
#servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id)
|
|
||||||
## TODO: this query needs to be narrower
|
|
||||||
servers = set()
|
|
||||||
#for s in servers_query:
|
|
||||||
# servers.add(s.server_id.server_id)
|
|
||||||
user['roles'] = roles
|
|
||||||
#user['servers'] = servers
|
|
||||||
#logger.debug("user: ({}) {}".format(user_id, user))
|
|
||||||
return user
|
return user
|
||||||
else:
|
else:
|
||||||
#logger.debug("user: ({}) {}".format(user_id, {}))
|
#logger.debug("user: ({}) {}".format(user_id, {}))
|
||||||
|
@ -23,8 +23,8 @@ try:
|
|||||||
import pexpect
|
import pexpect
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -130,8 +130,7 @@ class Server:
|
|||||||
self.process = pexpect.spawn(self.server_command, cwd=self.server_path, timeout=None, encoding=None)
|
self.process = pexpect.spawn(self.server_command, cwd=self.server_path, timeout=None, encoding=None)
|
||||||
self.is_crashed = False
|
self.is_crashed = False
|
||||||
|
|
||||||
ts = time.time()
|
self.start_time = str(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
self.start_time = str(datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'))
|
|
||||||
|
|
||||||
if psutil.pid_exists(self.process.pid):
|
if psutil.pid_exists(self.process.pid):
|
||||||
self.PID = self.process.pid
|
self.PID = self.process.pid
|
||||||
|
@ -21,7 +21,7 @@ try:
|
|||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
scheduler_intervals = { 'seconds',
|
scheduler_intervals = { 'seconds',
|
||||||
|
@ -11,14 +11,21 @@ logger = logging.getLogger(__name__)
|
|||||||
class Translation():
|
class Translation():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.translations_path = os.path.join(helper.root_dir, 'app', 'translations')
|
self.translations_path = os.path.join(helper.root_dir, 'app', 'translations')
|
||||||
|
self.cached_translation = None
|
||||||
|
self.cached_translation_lang = None
|
||||||
def translate(self, page, word):
|
def translate(self, page, word):
|
||||||
translated_word = None
|
translated_word = None
|
||||||
lang = helper.get_setting('language')
|
lang = helper.get_setting('language')
|
||||||
fallback_lang = 'en_EN'
|
fallback_lang = 'en_EN'
|
||||||
|
|
||||||
translated_word = \
|
lang_file_exists = helper.check_file_exists(
|
||||||
self.translate_inner(page, word, lang) or \
|
os.path.join(
|
||||||
self.translate_inner(page, word, fallback_lang)
|
self.translations_path, lang + '.json'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
translated_word = self.translate_inner(page, word, lang) \
|
||||||
|
if lang_file_exists else self.translate_inner(page, word, fallback_lang)
|
||||||
|
|
||||||
if translated_word:
|
if translated_word:
|
||||||
if isinstance(translated_word, dict): return json.dumps(translated_word)
|
if isinstance(translated_word, dict): return json.dumps(translated_word)
|
||||||
@ -31,8 +38,17 @@ class Translation():
|
|||||||
lang + '.json'
|
lang + '.json'
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
with open(lang_file, 'r') as f:
|
if not self.cached_translation:
|
||||||
data = json.load(f)
|
with open(lang_file, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
self.cached_translation = data
|
||||||
|
elif self.cached_translation_lang != lang:
|
||||||
|
with open(lang_file, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
self.cached_translation = data
|
||||||
|
self.cached_translation_lang = lang
|
||||||
|
else:
|
||||||
|
data = self.cached_translation
|
||||||
|
|
||||||
try:
|
try:
|
||||||
translated_page = data[page]
|
translated_page = data[page]
|
||||||
|
@ -6,53 +6,63 @@ import tornado.escape
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.classes.web.base_handler import BaseHandler
|
from app.classes.web.base_handler import BaseHandler
|
||||||
from app.classes.shared.models import Users
|
from app.classes.shared.models import db_shortcuts
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ApiHandler(BaseHandler):
|
class ApiHandler(BaseHandler):
|
||||||
|
|
||||||
def return_response(self, data: dict):
|
def return_response(self, status: int, data: dict):
|
||||||
# Define a standardized response
|
# Define a standardized response
|
||||||
|
self.set_status(status)
|
||||||
self.write(data)
|
self.write(data)
|
||||||
|
|
||||||
def access_denied(self, user):
|
def access_denied(self, user, reason=''):
|
||||||
log.info("User %s was denied access to API route", user)
|
if reason: reason = ' because ' + reason
|
||||||
self.set_status(403)
|
log.info("User %s from IP %s was denied access to the API route " + self.request.path + reason, user, self.get_remote_ip())
|
||||||
self.finish(self.return_response(403, {'error':'ACCESS_DENIED', 'info':'You were denied access to the requested resource'}))
|
self.finish(self.return_response(403, {
|
||||||
|
'error':'ACCESS_DENIED',
|
||||||
|
'info':'You were denied access to the requested resource'
|
||||||
|
}))
|
||||||
|
|
||||||
def authenticate_user(self):
|
def authenticate_user(self) -> bool:
|
||||||
try:
|
try:
|
||||||
log.debug("Searching for specified token")
|
log.debug("Searching for specified token")
|
||||||
# TODO: YEET THIS
|
# TODO: YEET THIS
|
||||||
user_data = Users.get(api_token=self.get_argument('token'))
|
user_data = db_shortcuts.get_user_by_api_token(self.get_argument('token'))
|
||||||
log.debug("Checking results")
|
log.debug("Checking results")
|
||||||
if user_data:
|
if user_data:
|
||||||
# Login successful! Check perms
|
# Login successful! Check perms
|
||||||
log.info("User {} has authenticated to API".format(user_data.username))
|
log.info("User {} has authenticated to API".format(user_data['username']))
|
||||||
# TODO: Role check
|
# TODO: Role check
|
||||||
|
|
||||||
|
return True # This is to set the "authenticated"
|
||||||
else:
|
else:
|
||||||
logging.debug("Auth unsuccessful")
|
logging.debug("Auth unsuccessful")
|
||||||
return self.access_denied("unknown")
|
self.access_denied("unknown", "the user provided an invalid token")
|
||||||
except:
|
return False
|
||||||
log.warning("Traceback occurred when authenticating user to API. Most likely wrong token")
|
except Exception as e:
|
||||||
return self.access_denied("unknown")
|
log.warning("An error occured while authenticating an API user: %s", e)
|
||||||
pass
|
self.access_denied("unknown"), "an error occured while authenticating the user"
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ServersStats(ApiHandler):
|
class ServersStats(ApiHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Get details about all servers"""
|
"""Get details about all servers"""
|
||||||
self.authenticate_user()
|
authenticated = self.authenticate_user()
|
||||||
|
if not authenticated: return
|
||||||
# Get server stats
|
# Get server stats
|
||||||
|
# TODO Check perms
|
||||||
self.finish(self.write({"servers": self.controller.stats.get_servers_stats()}))
|
self.finish(self.write({"servers": self.controller.stats.get_servers_stats()}))
|
||||||
|
|
||||||
|
|
||||||
class NodeStats(ApiHandler):
|
class NodeStats(ApiHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Get stats for particular node"""
|
"""Get stats for particular node"""
|
||||||
self.authenticate_user()
|
authenticated = self.authenticate_user()
|
||||||
|
if not authenticated: return
|
||||||
# Get node stats
|
# Get node stats
|
||||||
node_stats = self.controller.stats.get_node_stats()
|
node_stats = self.controller.stats.get_node_stats()
|
||||||
node_stats.pop("servers")
|
node_stats.pop("servers")
|
||||||
|
@ -61,7 +61,7 @@ class PanelHandler(BaseHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# if no servers defined, let's go to the build server area
|
# if no servers defined, let's go to the build server area
|
||||||
if page_data['server_stats']['total'] == 0 and page != "error":
|
if page_data['server_stats']['total'] == 0 and page != "error" and page != "credits" and page != "contribute":
|
||||||
self.set_status(301)
|
self.set_status(301)
|
||||||
self.redirect("/server/step1")
|
self.redirect("/server/step1")
|
||||||
return
|
return
|
||||||
|
@ -15,8 +15,8 @@ try:
|
|||||||
import bleach
|
import bleach
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -81,13 +81,13 @@ class PublicHandler(BaseHandler):
|
|||||||
|
|
||||||
# if we don't have a user
|
# if we don't have a user
|
||||||
if not user_data:
|
if not user_data:
|
||||||
next_page = "/public/error?error=Login_Failed"
|
next_page = "/public/error?error=Login Failed"
|
||||||
self.redirect(next_page)
|
self.redirect(next_page)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# if they are disabled
|
# if they are disabled
|
||||||
if not user_data.enabled:
|
if not user_data.enabled:
|
||||||
next_page = "/public/error?error=Login_Failed"
|
next_page = "/public/error?error=Login Failed"
|
||||||
self.redirect(next_page)
|
self.redirect(next_page)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -117,6 +117,10 @@ class PublicHandler(BaseHandler):
|
|||||||
|
|
||||||
next_page = "/panel/dashboard"
|
next_page = "/panel/dashboard"
|
||||||
self.redirect(next_page)
|
self.redirect(next_page)
|
||||||
|
else:
|
||||||
|
# log this failed login attempt
|
||||||
|
db_helper.add_to_audit_log(user_data.user_id, "Tried to log in", 0, self.get_remote_ip())
|
||||||
|
self.redirect('/public/error?error=Login Failed')
|
||||||
else:
|
else:
|
||||||
self.redirect("/public/login")
|
self.redirect("/public/login")
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ try:
|
|||||||
import bleach
|
import bleach
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
|
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
|
||||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -163,6 +163,10 @@ class ServerHandler(BaseHandler):
|
|||||||
import_server_jar = bleach.clean(self.get_argument('server_jar', ''))
|
import_server_jar = bleach.clean(self.get_argument('server_jar', ''))
|
||||||
server_parts = server.split("|")
|
server_parts = server.split("|")
|
||||||
|
|
||||||
|
if not server_name:
|
||||||
|
self.redirect("/panel/error?error=Server name cannot be empty!")
|
||||||
|
return False
|
||||||
|
|
||||||
if import_type == 'import_jar':
|
if import_type == 'import_jar':
|
||||||
good_path = self.controller.verify_jar_server(import_server_path, import_server_jar)
|
good_path = self.controller.verify_jar_server(import_server_path, import_server_jar)
|
||||||
|
|
||||||
@ -171,7 +175,12 @@ class ServerHandler(BaseHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
||||||
|
db_helper.add_to_audit_log(exec_user_data['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())
|
||||||
elif import_type == 'import_zip':
|
elif import_type == 'import_zip':
|
||||||
|
# here import_server_path means the zip path
|
||||||
good_path = self.controller.verify_zip_server(import_server_path)
|
good_path = self.controller.verify_zip_server(import_server_path)
|
||||||
if not good_path:
|
if not good_path:
|
||||||
self.redirect("/panel/error?error=Zip file not found!")
|
self.redirect("/panel/error?error=Zip file not found!")
|
||||||
@ -179,28 +188,24 @@ class ServerHandler(BaseHandler):
|
|||||||
|
|
||||||
new_server_id = self.controller.import_zip_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
new_server_id = self.controller.import_zip_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
||||||
if new_server_id == "false":
|
if new_server_id == "false":
|
||||||
self.redirect("/panel/error?error=ZIP file not accessible! You can fix this permissions issue with sudo chown -R crafty:crafty {} And sudo chmod 2775 -R {}".format(import_server_path, import_server_path))
|
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 False
|
return False
|
||||||
|
db_helper.add_to_audit_log(exec_user_data['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())
|
||||||
else:
|
else:
|
||||||
|
if len(server_parts) != 2:
|
||||||
|
self.redirect("/panel/error?error=Invalid server data")
|
||||||
|
return False
|
||||||
|
server_type, server_version = server_parts
|
||||||
# todo: add server type check here and call the correct server add functions if not a jar
|
# todo: add server type check here and call the correct server add functions if not a jar
|
||||||
new_server_id = self.controller.create_jar_server(server_parts[0], server_parts[1], server_name, min_mem, max_mem, port)
|
new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port)
|
||||||
|
|
||||||
if new_server_id is not None and exec_user_data is not None and len(server_parts) > 1:
|
|
||||||
db_helper.add_to_audit_log(exec_user_data['user_id'],
|
db_helper.add_to_audit_log(exec_user_data['user_id'],
|
||||||
"created a {} {} server named \"{}\"".format(server_parts[1], str(server_parts[0]).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival"
|
"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,
|
new_server_id,
|
||||||
self.get_remote_ip())
|
self.get_remote_ip())
|
||||||
|
|
||||||
elif new_server_id is not None and exec_user_data is not None:
|
|
||||||
db_helper.add_to_audit_log(exec_user_data['user_id'],
|
|
||||||
"created a {} {} server named \"{}\"".format("Minecraft", str(server_parts[0]).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival"
|
|
||||||
new_server_id,
|
|
||||||
self.get_remote_ip())
|
|
||||||
|
|
||||||
else:
|
|
||||||
logger.error("Unable to create server")
|
|
||||||
console.error("Unable to create server")
|
|
||||||
|
|
||||||
self.controller.stats.record_stats()
|
self.controller.stats.record_stats()
|
||||||
self.redirect("/panel/dashboard")
|
self.redirect("/panel/dashboard")
|
||||||
|
|
||||||
|
15
app/classes/web/static_handler.py
Normal file
15
app/classes/web/static_handler.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import tornado.web
|
||||||
|
from typing import (
|
||||||
|
Optional
|
||||||
|
)
|
||||||
|
|
||||||
|
from app.classes.shared.console import console
|
||||||
|
|
||||||
|
class CustomStaticHandler(tornado.web.StaticFileHandler):
|
||||||
|
def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
|
||||||
|
try:
|
||||||
|
return super().validate_absolute_path(root, absolute_path)
|
||||||
|
except tornado.web.HTTPError as error:
|
||||||
|
if 'HTTP 404: Not Found' in str(error):
|
||||||
|
self.set_status(404)
|
||||||
|
self.finish({'error':'NOT_FOUND', 'info':'The requested resource was not found on the server'})
|
@ -25,6 +25,7 @@ try:
|
|||||||
from app.classes.web.ajax_handler import AjaxHandler
|
from app.classes.web.ajax_handler import AjaxHandler
|
||||||
from app.classes.web.api_handler import ServersStats, NodeStats
|
from app.classes.web.api_handler import ServersStats, NodeStats
|
||||||
from app.classes.web.websocket_handler import SocketHandler
|
from app.classes.web.websocket_handler import SocketHandler
|
||||||
|
from app.classes.web.static_handler import CustomStaticHandler
|
||||||
from app.classes.shared.translation import translation
|
from app.classes.shared.translation import translation
|
||||||
|
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
@ -112,9 +113,6 @@ class Webserver:
|
|||||||
|
|
||||||
logger.info("Starting Web Server on ports http:{} https:{}".format(http_port, https_port))
|
logger.info("Starting Web Server on ports http:{} https:{}".format(http_port, https_port))
|
||||||
|
|
||||||
console.info("http://{}:{} is up and ready for connection:".format(helper.get_local_ip(), http_port))
|
|
||||||
console.info("https://{}:{} is up and ready for connection:".format(helper.get_local_ip(), https_port))
|
|
||||||
|
|
||||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||||
|
|
||||||
tornado.template.Loader('.')
|
tornado.template.Loader('.')
|
||||||
@ -143,7 +141,9 @@ class Webserver:
|
|||||||
autoreload=False,
|
autoreload=False,
|
||||||
log_function=self.log_function,
|
log_function=self.log_function,
|
||||||
login_url="/login",
|
login_url="/login",
|
||||||
default_handler_class=PublicHandler
|
default_handler_class=PublicHandler,
|
||||||
|
static_handler_class=CustomStaticHandler,
|
||||||
|
serve_traceback=debug_errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.HTTP_Server = tornado.httpserver.HTTPServer(app)
|
self.HTTP_Server = tornado.httpserver.HTTPServer(app)
|
||||||
@ -152,6 +152,11 @@ class Webserver:
|
|||||||
self.HTTPS_Server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
|
self.HTTPS_Server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
|
||||||
self.HTTPS_Server.listen(https_port)
|
self.HTTPS_Server.listen(https_port)
|
||||||
|
|
||||||
|
logger.info("http://{}:{} is up and ready for connections.".format(helper.get_local_ip(), http_port))
|
||||||
|
logger.info("https://{}:{} is up and ready for connections.".format(helper.get_local_ip(), https_port))
|
||||||
|
console.info("http://{}:{} is up and ready for connections.".format(helper.get_local_ip(), http_port))
|
||||||
|
console.info("https://{}:{} is up and ready for connections.".format(helper.get_local_ip(), https_port))
|
||||||
|
|
||||||
console.info("Server Init Complete: Listening For Connections:")
|
console.info("Server Init Complete: Listening For Connections:")
|
||||||
|
|
||||||
self.ioloop = tornado.ioloop.IOLoop.instance()
|
self.ioloop = tornado.ioloop.IOLoop.instance()
|
||||||
|
@ -43,10 +43,12 @@
|
|||||||
<a class="nav-link active" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=config" role="tab" aria-selected="true">
|
<a class="nav-link active" href="/panel/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>Config</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if data['new_user'] %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=other" role="tab" aria-selected="false">
|
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=other" role="tab" aria-selected="false">
|
||||||
<i class="fas fa-folder-tree"></i>Other</a>
|
<i class="fas fa-folder-tree"></i>Other</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% end %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -86,11 +86,8 @@
|
|||||||
let startedLocal;
|
let startedLocal;
|
||||||
|
|
||||||
if (started != null) {
|
if (started != null) {
|
||||||
console.log('88', '{{ data['server_stats']['started'] }}');
|
startedUTC = '{{ data['server_stats']['started'] }}';
|
||||||
{% if data['server_stats']['started'] != 'False' %}
|
console.log('started utc:', startedUTC);
|
||||||
startedUTC = '{{ (datetime.datetime.strptime(data['server_stats']['started'], '%Y-%m-%d %H:%M:%S') - datetime.timedelta(seconds=-time.timezone)).strftime('%Y-%m-%d %H:%M:%S') }}';
|
|
||||||
{% end %}
|
|
||||||
console.log('utc', startedUTC);
|
|
||||||
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
let browserUTCOffset = moment().utcOffset(); // This is in minutes
|
let browserUTCOffset = moment().utcOffset(); // This is in minutes
|
||||||
@ -98,32 +95,25 @@
|
|||||||
startedLocal = startedUTC.utcOffset(browserUTCOffset);
|
startedLocal = startedUTC.utcOffset(browserUTCOffset);
|
||||||
startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss');
|
startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
console.log('startedLocal', startedLocal);
|
console.log('started local time:', startedLocalFormatted);
|
||||||
console.log('startedLocalFormatted', startedLocalFormatted);
|
|
||||||
|
|
||||||
started.textContent = startedLocalFormatted
|
started.textContent = startedLocalFormatted
|
||||||
}
|
}
|
||||||
|
|
||||||
let nowServerTime = '{{ data['time'] }}';
|
var calculateUptime = () => {
|
||||||
let startedServerTime = '{{ data['server_stats']['started'] }}';
|
var msdiff = moment()
|
||||||
|
.diff(startedLocal);
|
||||||
if (uptime != null && started != null) {
|
|
||||||
|
|
||||||
var msdiff = moment(nowServerTime,"YYYY-MM-DD hh:mm:ss")
|
|
||||||
.diff(moment(startedServerTime,"YYYY-MM-DD hh:mm:ss"));
|
|
||||||
var diff = moment.duration(msdiff);
|
var diff = moment.duration(msdiff);
|
||||||
|
|
||||||
uptime.textContent = durationToHumanizedString(diff);
|
uptime.textContent = durationToHumanizedString(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uptime != null && started != null) {
|
||||||
|
|
||||||
console.log('startedLocal', startedLocal)
|
console.log('startedLocal', startedLocal)
|
||||||
if (startedLocal) {
|
if (startedLocal) {
|
||||||
var uptimeLoop = setInterval(() => {
|
calculateUptime()
|
||||||
var msdiff = moment()
|
var uptimeLoop = setInterval(calculateUptime, 1000)
|
||||||
.diff(startedLocal);
|
|
||||||
var diff = moment.duration(msdiff);
|
|
||||||
|
|
||||||
uptime.textContent = durationToHumanizedString(diff);
|
|
||||||
}, 1000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +194,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<h2 id="fileError"></h2>
|
||||||
<div id="editorParent">
|
<div id="editorParent">
|
||||||
{{ translate('serverFiles', 'editingFile') }} <span id="editingFile"></span>
|
{{ translate('serverFiles', 'editingFile') }} <span id="editingFile"></span>
|
||||||
<div id="editor" onresize="editor.resize()" style="resize: both;width: 100%;">file_contents</div>
|
<div id="editor" onresize="editor.resize()" style="resize: both;width: 100%;">file_contents</div>
|
||||||
@ -344,10 +345,13 @@
|
|||||||
console.log('Got File Contents From Server');
|
console.log('Got File Contents From Server');
|
||||||
json = JSON.parse(data)
|
json = JSON.parse(data)
|
||||||
if (json.error) {
|
if (json.error) {
|
||||||
$('#editorParent').toggle(false)
|
$('#editorParent').toggle(false) // hide
|
||||||
|
$('#fileError').toggle(true) // show
|
||||||
|
$('#fileError').text("{{ translate('serverFiles', 'fileReadError') }}: " + json.error) // show error
|
||||||
editor.blur()
|
editor.blur()
|
||||||
} else {
|
} else {
|
||||||
$('#editorParent').toggle(true)
|
$('#editorParent').toggle(true) // show
|
||||||
|
$('#fileError').toggle(false) // hide
|
||||||
setFileName(event.target.innerText);
|
setFileName(event.target.innerText);
|
||||||
editor.session.setValue(json.content);
|
editor.session.setValue(json.content);
|
||||||
}
|
}
|
||||||
@ -376,7 +380,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFileName();
|
setFileName();
|
||||||
$('#editorParent').toggle(false)
|
$('#editorParent').toggle(false) // show
|
||||||
|
$('#fileError').toggle(false) // hide
|
||||||
editor.blur()
|
editor.blur()
|
||||||
|
|
||||||
function setMode (extension) {
|
function setMode (extension) {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<p class="card-description">
|
<p class="card-description">
|
||||||
|
|
||||||
<form method="post">
|
<form method="post" class="server-wizard">
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@ -47,22 +47,22 @@
|
|||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="min_memory1">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="min_memory1" name="min_memory" value="1" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-3 offset-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="max_memory1">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="max_memory1" name="max_memory" value="2" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-3 offset-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
<label for="port1">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
||||||
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
|
<input type="number" class="form-control" id="port1" name="port" value="25565" step="1" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -84,7 +84,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<p class="card-description">
|
<p class="card-description">
|
||||||
|
|
||||||
<form method="post">
|
<form method="post" class="server-wizard">
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<input type="hidden" value="import_jar" name="create_type">
|
<input type="hidden" value="import_jar" name="create_type">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -120,22 +120,22 @@
|
|||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="min_memory2">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="min_memory2" name="min_memory" value="1" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-3 offset-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="max_memory2">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="max_memory2" name="max_memory" value="2" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-3 offset-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
<label for="port2">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
||||||
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
|
<input type="number" class="form-control" id="port2" name="port" value="25565" step="1" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -157,7 +157,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<p class="card-description">
|
<p class="card-description">
|
||||||
|
|
||||||
<form method="post">
|
<form method="post" class="server-wizard">
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<input type="hidden" value="import_zip" name="create_type">
|
<input type="hidden" value="import_zip" name="create_type">
|
||||||
|
|
||||||
@ -194,22 +194,22 @@
|
|||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="min_memory3">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="min_memory3" name="min_memory" value="1" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
<label for="max_memory3">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
|
<input type="number" class="form-control" id="max_memory3" name="max_memory" value="2" step="0.5" min="0.5">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
<label for="port3">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
|
||||||
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
|
<input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -231,12 +231,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
$( document ).ready(function() {
|
$( document ).ready(function() {
|
||||||
console.log('ready');
|
console.log('ready');
|
||||||
$("#min_memory").change(function(){
|
var forms = $('form.server-wizard');
|
||||||
check_sizes('min');
|
forms.each(function(i, formEl) {
|
||||||
});
|
var form = $(formEl);
|
||||||
$("#max_memory").change(function(){
|
var min = form.find('[name=min_memory]');
|
||||||
check_sizes('max');
|
var max = form.find('[name=max_memory]');
|
||||||
|
console.log(form, min, max)
|
||||||
|
min.change(function(){
|
||||||
|
check_sizes(max, min, 'min');
|
||||||
|
});
|
||||||
|
max.change(function(){
|
||||||
|
check_sizes(max, min, 'max');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -247,14 +254,14 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function check_sizes(changed){
|
function check_sizes(a, b, changed){
|
||||||
max_mem = parseFloat($('#max_memory').val());
|
max_mem = parseFloat(a.val());
|
||||||
min_mem = parseFloat($('#min_memory').val());
|
min_mem = parseFloat(b.val());
|
||||||
if (max_mem < min_mem && changed === 'min'){
|
if (max_mem < min_mem && changed === 'min'){
|
||||||
$('#max_memory').val(min_mem)
|
a.val(min_mem)
|
||||||
}
|
}
|
||||||
if (max_mem < min_mem && changed === 'max'){
|
if (max_mem < min_mem && changed === 'max'){
|
||||||
$('#min_memory').val(max_mem)
|
b.val(max_mem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"errorCalculatingUptime": "Error Calculating Uptime",
|
"errorCalculatingUptime": "Error Calculating Uptime",
|
||||||
"serverTime": "Server Time",
|
"serverTime": "UTC Time",
|
||||||
"unableToConnect": "Unable To Connect"
|
"unableToConnect": "Unable To Connect"
|
||||||
},
|
},
|
||||||
"serverDetails": {
|
"serverDetails": {
|
||||||
@ -170,7 +170,8 @@
|
|||||||
"yesDelete": "Yes, I understand the consequences",
|
"yesDelete": "Yes, I understand the consequences",
|
||||||
"noDelete": "No",
|
"noDelete": "No",
|
||||||
"unsupportedLanguage": "Warning: This is not a supported file type",
|
"unsupportedLanguage": "Warning: This is not a supported file type",
|
||||||
"keybindings": "Keybindings"
|
"keybindings": "Keybindings",
|
||||||
|
"fileReadError": "File read error"
|
||||||
},
|
},
|
||||||
"serverConfig": {
|
"serverConfig": {
|
||||||
"serverName": "Server Name",
|
"serverName": "Server Name",
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
"version": "Versio",
|
"version": "Versio",
|
||||||
"description": "Kuvaus",
|
"description": "Kuvaus",
|
||||||
"errorCalculatingUptime": "Virhe laskettaessa käyttöaikaa",
|
"errorCalculatingUptime": "Virhe laskettaessa käyttöaikaa",
|
||||||
"serverTime": "Palvelimen aikaa",
|
"serverTime": "UTC aikaa",
|
||||||
"unableToConnect": "Yhteyden muodostaminen epäonnistui"
|
"unableToConnect": "Yhteyden muodostaminen epäonnistui"
|
||||||
},
|
},
|
||||||
"serverDetails": {
|
"serverDetails": {
|
||||||
@ -166,11 +166,12 @@
|
|||||||
"createDirQuestion": "Minkä nimen haluat uudelle hakemistolle?",
|
"createDirQuestion": "Minkä nimen haluat uudelle hakemistolle?",
|
||||||
"renameItemQuestion": "Mikä uuden nimen pitäisi olla?",
|
"renameItemQuestion": "Mikä uuden nimen pitäisi olla?",
|
||||||
"deleteItemQuestion": "Haluatko varmasti poistaa \" + name + \"?",
|
"deleteItemQuestion": "Haluatko varmasti poistaa \" + name + \"?",
|
||||||
"deleteItemQuestionMessage": "Olet poistamassa \\\"\" + path + \"\\\"!<br/><br/>Tämä toiminta on peruuttamaton ja se menetetään ikuisesti!",
|
"deleteItemQuestionMessage": "Olet poistamassa \\\"\" + path + \"\\\"!<br/><br/>Tämä toiminto on peruuttamaton ja se menetetään ikuisesti!",
|
||||||
"yesDelete": "Kyllä, ymmärrän seuraukset",
|
"yesDelete": "Kyllä, ymmärrän seuraukset",
|
||||||
"noDelete": "En",
|
"noDelete": "En",
|
||||||
"unsupportedLanguage": "Varoitus: Tätä tiedostotyyppiä ei tueta",
|
"unsupportedLanguage": "Varoitus: Tätä tiedostotyyppiä ei tueta",
|
||||||
"keybindings": "Pikanäppäimet"
|
"keybindings": "Pikanäppäimet",
|
||||||
|
"fileReadError": "Tiedoston lukuvirhe"
|
||||||
},
|
},
|
||||||
"serverConfig": {
|
"serverConfig": {
|
||||||
"serverName": "Palvelimen nimi",
|
"serverName": "Palvelimen nimi",
|
||||||
|
8
main.py
8
main.py
@ -141,10 +141,4 @@ if __name__ == '__main__':
|
|||||||
logger.info("Recieved SIGINT, stopping Crafty")
|
logger.info("Recieved SIGINT, stopping Crafty")
|
||||||
break
|
break
|
||||||
|
|
||||||
logger.info("Stopping all server daemons / threads")
|
Crafty.universal_exit()
|
||||||
console.info("Stopping all server daemons / threads - This may take a few seconds")
|
|
||||||
Crafty._clean_shutdown()
|
|
||||||
while True:
|
|
||||||
if tasks_manager.get_main_thread_run_status():
|
|
||||||
sys.exit(0)
|
|
||||||
time.sleep(1)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user