mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Backup Restore/Root Disable
This commit is contained in:
parent
798dac02a5
commit
a19ba7dbb6
@ -51,6 +51,17 @@ class Server_Perms_Controller:
|
||||
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
||||
return server_permissions.add_role_server(server_id, role_id, rs_permissions)
|
||||
|
||||
@staticmethod
|
||||
def get_server_roles(server_id):
|
||||
return server_permissions.get_server_roles(server_id)
|
||||
|
||||
@staticmethod
|
||||
def backup_role_swap(old_server_id, new_server_id):
|
||||
role_list = server_permissions.get_server_roles(old_server_id)
|
||||
for role in role_list:
|
||||
server_permissions.add_role_server(new_server_id, role.role_id, server_permissions.get_permissions_mask(int(role.role_id), int(old_server_id)))
|
||||
#server_permissions.add_role_server(new_server_id, role.role_id, '00001000')
|
||||
|
||||
#************************************************************************************************
|
||||
# Servers Permissions Methods
|
||||
#************************************************************************************************
|
||||
|
@ -227,6 +227,10 @@ class helpers_management:
|
||||
def update_scheduled_task(schedule_id, updates):
|
||||
Schedules.update(updates).where(Schedules.schedule_id == schedule_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def delete_scheduled_task_by_server(server_id):
|
||||
Schedules.delete().where(Schedules.server_id == int(server_id)).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_scheduled_task(schedule_id):
|
||||
return model_to_dict(Schedules.get(Schedules.schedule_id == schedule_id)).execute()
|
||||
|
@ -118,10 +118,18 @@ class Permissions_Servers:
|
||||
@staticmethod
|
||||
def get_permissions_mask(role_id, server_id):
|
||||
permissions_mask = ''
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).execute()
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get()
|
||||
permissions_mask = role_server.permissions
|
||||
return permissions_mask
|
||||
|
||||
@staticmethod
|
||||
def get_server_roles(server_id):
|
||||
role_list = []
|
||||
roles = Role_Servers.select().where(Role_Servers.server_id == server_id).execute()
|
||||
for role in roles:
|
||||
role_list.append(role.role_id)
|
||||
return role_list
|
||||
|
||||
@staticmethod
|
||||
def get_role_permissions_list(role_id):
|
||||
permissions_mask = '00000000'
|
||||
|
@ -338,6 +338,12 @@ class Helpers:
|
||||
logger.critical("Unable to write to {} - Error: {}".format(path, e))
|
||||
return False
|
||||
|
||||
def checkRoot(self):
|
||||
if os.geteuid() == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def unzipFile(self, zip_path):
|
||||
new_dir_list = zip_path.split('/')
|
||||
new_dir = ''
|
||||
|
@ -1,13 +1,17 @@
|
||||
import os
|
||||
import pathlib
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from peewee import DoesNotExist
|
||||
import schedule
|
||||
import yaml
|
||||
import asyncio
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
from distutils import dir_util
|
||||
from app.classes.models.management import helpers_management
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
@ -285,11 +289,32 @@ class Controller:
|
||||
helper.ensure_dir_exists(new_server_dir)
|
||||
helper.ensure_dir_exists(backup_path)
|
||||
tempDir = tempfile.mkdtemp()
|
||||
has_properties = False
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
#extracts archive to temp directory
|
||||
zip_ref.extractall(tempDir)
|
||||
if len(zip_ref.filelist) > 1:
|
||||
for item in os.listdir(tempDir):
|
||||
if str(item) == 'server.properties':
|
||||
has_properties = True
|
||||
try:
|
||||
shutil.move(os.path.join(tempDir, item), os.path.join(new_server_dir, item))
|
||||
except Exception as ex:
|
||||
logger.error('ERROR IN ZIP IMPORT: {}'.format(ex))
|
||||
if not has_properties:
|
||||
logger.info("No server.properties found on zip file import. Creating one with port selection of {}".format(str(port)))
|
||||
with open(os.path.join(new_server_dir, "server.properties"), "w") as f:
|
||||
f.write("server-port={}".format(port))
|
||||
f.close()
|
||||
zip_ref.close()
|
||||
else:
|
||||
|
||||
#iterates list of files
|
||||
for i in range(len(zip_ref.filelist)):
|
||||
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'):
|
||||
test = zip_ref.filelist[i].filename
|
||||
#checks if the list of files inside of a dir is greater than 1 or if it's not a directory.
|
||||
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].is_dir():
|
||||
#sets local variable to be that filename and we break out of the loop since we found our root dir.
|
||||
test = zip_ref.filelist[i-1].filename
|
||||
break
|
||||
path_list = test.split('/')
|
||||
root_path = path_list[0]
|
||||
@ -299,7 +324,7 @@ class Controller:
|
||||
|
||||
full_root_path = os.path.join(tempDir, root_path)
|
||||
|
||||
has_properties = False
|
||||
|
||||
for item in os.listdir(full_root_path):
|
||||
if str(item) == 'server.properties':
|
||||
has_properties = True
|
||||
@ -328,10 +353,20 @@ class Controller:
|
||||
server_log_file, server_stop, port)
|
||||
return new_id
|
||||
|
||||
def rename_backup_dir(self, old_server_id, new_server_id, new_uuid):
|
||||
server_data = self.servers.get_server_data_by_id(old_server_id)
|
||||
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)
|
||||
backup_path_components = list(backup_path.parts)
|
||||
backup_path_components[-1] = new_uuid
|
||||
new_bu_path = pathlib.PurePath(os.path.join(*backup_path_components))
|
||||
backup_path.rename(new_bu_path)
|
||||
|
||||
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:
|
||||
# place a file in the dir saying it's owned by crafty
|
||||
with open(os.path.join(server_dir, "crafty_managed.txt"), 'w') as f:
|
||||
@ -356,6 +391,7 @@ class Controller:
|
||||
if int(s['server_id']) == int(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']
|
||||
|
||||
logger.info("Deleting Server: ID {} | Name: {} ".format(server_id, server_name))
|
||||
console.info("Deleting Server: ID {} | Name: {} ".format(server_id, server_name))
|
||||
@ -366,7 +402,19 @@ class Controller:
|
||||
if running:
|
||||
self.stop_server(server_id)
|
||||
if files:
|
||||
try:
|
||||
shutil.rmtree(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['path']))
|
||||
except Exception as e:
|
||||
logger.error("Unable to delete server files for server with ID: {} with error logged: {}".format(server_id, e))
|
||||
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)
|
||||
except DoesNotExist:
|
||||
logger.info("No scheduled jobs exist. Continuing.")
|
||||
# remove the server from the DB
|
||||
self.servers.remove_server(server_id)
|
||||
|
||||
@ -374,3 +422,4 @@ class Controller:
|
||||
self.servers_list.pop(counter)
|
||||
|
||||
counter += 1
|
||||
return
|
||||
|
@ -243,6 +243,15 @@ class Server:
|
||||
try:
|
||||
self.process = subprocess.Popen(self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
except Exception as ex:
|
||||
#Checks for java on initial fail
|
||||
if os.system("java -version") == 32512:
|
||||
msg = "Server {} failed to start with error code: {}".format(self.name, "Java not found. Please install Java then try again.")
|
||||
if user_id:
|
||||
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
||||
'error': translation.translate('error', 'noJava', user_lang).format(self.name)
|
||||
})
|
||||
return False
|
||||
else:
|
||||
msg = "Server {} failed to start with error code: {}".format(self.name, ex)
|
||||
logger.error(msg)
|
||||
if user_id:
|
||||
@ -512,14 +521,14 @@ class Server:
|
||||
return
|
||||
|
||||
def list_backups(self):
|
||||
conf = management_helper.get_backup_config(self.server_id)
|
||||
if self.settings['backup_path']:
|
||||
if helper.check_path_exists(helper.get_os_understandable_path(self.settings['backup_path'])):
|
||||
files = helper.get_human_readable_files_sizes(helper.list_dir_by_date(helper.get_os_understandable_path(self.settings['backup_path'])))
|
||||
return [{"path": os.path.relpath(f['path'], start=helper.get_os_understandable_path(conf['backup_path'])), "size": f["size"]} for f in files]
|
||||
return [{"path": os.path.relpath(f['path'], start=helper.get_os_understandable_path(self.settings['backup_path'])), "size": f["size"]} for f in files]
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
logger.info("Error putting backup file list for server with ID: {}".format(self.server_id))
|
||||
return[]
|
||||
|
||||
def jar_update(self):
|
||||
|
@ -212,6 +212,21 @@ class AjaxHandler(BaseHandler):
|
||||
svr = self.controller.get_server_obj(server_id)
|
||||
svr.agree_eula(user_data['user_id'])
|
||||
|
||||
elif page == "restore_backup":
|
||||
server_id = bleach.clean(self.get_argument('id', None))
|
||||
zip_name = bleach.clean(self.get_argument('zip_file', None))
|
||||
svr_obj = self.controller.servers.get_server_obj(server_id)
|
||||
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
||||
backup_path = svr_obj.backup_path
|
||||
if helper.validate_traversal(backup_path, zip_name):
|
||||
new_server = self.controller.import_zip_server(svr_obj.server_name, os.path.join(backup_path, zip_name), server_data['executable'], '1', '2', server_data['server_port'])
|
||||
new_server_id = new_server
|
||||
new_server = self.controller.get_server_data(new_server)
|
||||
self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid'])
|
||||
self.controller.remove_server(server_id, True)
|
||||
self.redirect('/panel/dashboard')
|
||||
|
||||
|
||||
@tornado.web.authenticated
|
||||
def delete(self, page):
|
||||
if page == "del_file":
|
||||
|
@ -121,13 +121,22 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
elif page == 'dashboard':
|
||||
if exec_user['superuser'] == 1:
|
||||
try:
|
||||
page_data['servers'] = self.controller.servers.get_all_servers_stats()
|
||||
except IndexError:
|
||||
self.controller.stats.record_stats()
|
||||
page_data['servers'] = self.controller.servers.get_all_servers_stats()
|
||||
|
||||
for data in page_data['servers']:
|
||||
try:
|
||||
data['stats']['waiting_start'] = self.controller.servers.get_waiting_start(int(data['stats']['server_id']['server_id']))
|
||||
except:
|
||||
data['stats']['waiting_start'] = False
|
||||
else:
|
||||
try:
|
||||
user_auth = self.controller.servers.get_authorized_servers_stats(exec_user_id)
|
||||
except IndexError:
|
||||
self.controller.stats.record_stats()
|
||||
user_auth = self.controller.servers.get_authorized_servers_stats(exec_user_id)
|
||||
logger.debug("ASFR: {}".format(user_auth))
|
||||
page_data['servers'] = user_auth
|
||||
@ -208,9 +217,12 @@ class PanelHandler(BaseHandler):
|
||||
if subpage == "backup":
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
page_data['backup_config'] = self.controller.management.get_backup_config(server_id)
|
||||
self.controller.refresh_server_settings(server_id)
|
||||
try:
|
||||
page_data['backup_list'] = server.list_backups()
|
||||
except:
|
||||
page_data['backup_list'] = []
|
||||
page_data['backup_path'] = helper.wtol_path(server_info["backup_path"])
|
||||
print(page_data['backup_path'])
|
||||
|
||||
def get_banned_players_html():
|
||||
banned_players = self.controller.servers.get_banned_players(server_id)
|
||||
@ -416,8 +428,6 @@ class PanelHandler(BaseHandler):
|
||||
return
|
||||
elif Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions:
|
||||
if str(user_id) != str(exec_user_id):
|
||||
print("USER ID ", user_id)
|
||||
print("EXEC ID ", exec_user_id)
|
||||
self.redirect("/panel/error?error=Unauthorized access: not a user editor")
|
||||
return
|
||||
|
||||
|
@ -45,8 +45,10 @@
|
||||
<a href="/panel/backup_now?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary" onclick="backup_started()">{{ translate('serverBackups', 'backupNow', data['lang']) }}</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{% if data['super_user'] %}
|
||||
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}" >
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@ -100,6 +102,10 @@
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
{{ translate('serverBackups', 'delete', data['lang']) }}
|
||||
</button>
|
||||
<button data-file="{{ backup['path'] }}" class="btn btn-warning restore_button">
|
||||
<i class="fas fa-undo-alt" aria-hidden="true"></i>
|
||||
{{ translate('serverBackups', 'restore', data['lang']) }}
|
||||
</button>
|
||||
</td>
|
||||
<td>{{ backup['path'] }}</td>
|
||||
<td>{{ backup['size'] }}</td>
|
||||
@ -162,6 +168,31 @@
|
||||
});
|
||||
}
|
||||
|
||||
function restore_backup(filename, id){
|
||||
var token = getCookie("_xsrf")
|
||||
|
||||
console.log('Sending Command to restore backup: ' + filename)
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/restore_backup?server_id='+id,
|
||||
data: {
|
||||
zip_file: filename,
|
||||
id: id
|
||||
},
|
||||
success: function(data) {
|
||||
var dialog = bootbox.dialog({
|
||||
message: '<i class="fa fa-spin fa-spinner"></i> {{ translate('serverBackups', 'restoring', data['lang']) }}',
|
||||
closeButton: false
|
||||
});
|
||||
setTimeout(function(){
|
||||
location.href=('/panel/dashboard');
|
||||
}, 15000);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
$("#backup_config_box").hide();
|
||||
@ -211,6 +242,30 @@
|
||||
});
|
||||
});
|
||||
|
||||
$( ".restore_button" ).click(function() {
|
||||
var file_to_restore = $(this).data("file");
|
||||
|
||||
|
||||
bootbox.confirm({
|
||||
title: "{{ translate('serverBackups', 'restore', data['lang']) }} "+file_to_restore,
|
||||
message: "{{ translate('serverBackups', 'confirmRestore', data['lang']) }}",
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
|
||||
},
|
||||
confirm: {
|
||||
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "restore", data['lang']) }}'
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
console.log(result);
|
||||
if (result == true) {
|
||||
restore_backup(file_to_restore, {{ data['server_stats']['server_id']['server_id'] }} );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -236,34 +236,16 @@ let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
function deleteServer (){
|
||||
path = "{{data['server_stats']['server_id']['path']}}";
|
||||
name = "{{data['server_stats']['server_id']['server_name']}}";
|
||||
bootbox.confirm({
|
||||
bootbox.dialog({
|
||||
size: "",
|
||||
title: "{% raw translate('serverConfig', 'deleteFilesQuestion', data['lang']) %}",
|
||||
closeButton: false,
|
||||
message: "{% raw translate('serverConfig', 'deleteFilesQuestionMessage', data['lang']) %}",
|
||||
buttons: {
|
||||
confirm: {
|
||||
files: {
|
||||
label: "{{ translate('serverConfig', 'yesDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-danger',
|
||||
},
|
||||
cancel: {
|
||||
label: "{{ translate('serverConfig', 'noDeleteFiles', data['lang']) }}",
|
||||
className: 'btn-link',
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result){
|
||||
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;}
|
||||
else{
|
||||
callback: function(){
|
||||
deleteServerFilesE();
|
||||
setTimeout(function(){ window.location = '/panel/dashboard'; }, 5000);
|
||||
bootbox.dialog({
|
||||
@ -272,7 +254,34 @@ let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -16,7 +16,8 @@
|
||||
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",
|
||||
"eulaTitle": "Agree To EULA",
|
||||
"eulaMsg": "You must agree to the EULA. A copy of the Mojang EULA is linked under this message.",
|
||||
"eulaAgree": "Do you agree?"
|
||||
"eulaAgree": "Do you agree?",
|
||||
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server."
|
||||
},
|
||||
"404": {
|
||||
"contact": "Contact Crafty Control Support via Discord",
|
||||
@ -178,7 +179,10 @@
|
||||
"destroyBackup": "Destroy backup \" + file_to_del + \"?",
|
||||
"confirmDelete": "Do you want to delete this backup? This cannot be undone.",
|
||||
"confirm": "Confirm",
|
||||
"options": "Options"
|
||||
"options": "Options",
|
||||
"restoring": "Restoring Backup. This may take a while. Please be patient.",
|
||||
"restore": "Restore",
|
||||
"confirmRestore": "Are you sure you want to restore from this backup. All current server files will changed to backup state and will be unrecoverable."
|
||||
},
|
||||
"serverFiles": {
|
||||
"noscript": "The file manager does not work without JavaScript",
|
||||
@ -247,7 +251,7 @@
|
||||
"yesDelete": "Yes, delete",
|
||||
"noDelete": "No, go back",
|
||||
"deleteFilesQuestion": "Delete server files from machine?",
|
||||
"deleteFilesQuestionMessage": "Would you like Crafty to delete all server files from the host machine?",
|
||||
"deleteFilesQuestionMessage": "Would you like Crafty to delete all server files from the host machine? <br><br><strong>This includes server backups.</strong>",
|
||||
"yesDeleteFiles": "Yes, delete files",
|
||||
"noDeleteFiles": "No, just remove from panel",
|
||||
"sendingDelete": "Deleting Server",
|
||||
|
@ -16,7 +16,8 @@
|
||||
"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.",
|
||||
"eulaAgree": "Oletko samaa mieltä?"
|
||||
"eulaAgree": "Oletko samaa mieltä?",
|
||||
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server."
|
||||
},
|
||||
"404": {
|
||||
"contact": "Ota yhteyttä Crafty Control -tukeen Discordin kautta",
|
||||
@ -179,7 +180,10 @@
|
||||
"destroyBackup": "Tuhotaanko varmuuskopio \" + file_to_del + \"?",
|
||||
"confirmDelete": "Haluatko poistaa tämän varmuuskopion? Tätä ei voi peruuttaa.",
|
||||
"confirm": "Vahvista",
|
||||
"options": "Vaihtoehtoja"
|
||||
"options": "Vaihtoehtoja",
|
||||
"restoring": "Varmuuskopion palauttaminen. Tämä voi kestää hetken. Olkaa kärsivällisiä.",
|
||||
"restore": "Palauttaa",
|
||||
"confirmRestore": "Haluatko varmasti palauttaa tämän varmuuskopion. Kaikki nykyiset palvelintiedostot muutetaan varmuuskopiotilaan, eikä niitä voida palauttaa."
|
||||
},
|
||||
"serverFiles": {
|
||||
"noscript": "Tiedostojenhallinta ei toimi ilman JavaScriptiä",
|
||||
@ -237,7 +241,23 @@
|
||||
"save": "Tallenna",
|
||||
"cancel": "Peruuta",
|
||||
"deleteServer": "Poista palvelin",
|
||||
"stopBeforeDeleting": "Pysäytä palvelin ennen sen poistamista"
|
||||
"stopBeforeDeleting": "Pysäytä palvelin ennen sen poistamista",
|
||||
"exeUpdateURLDesc": "Direct Download URL for updates.",
|
||||
"exeUpdateURL": "Palvelimen suoritettavan päivityksen URL-osoite",
|
||||
"update": "Päivitä suoritettava",
|
||||
"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...",
|
||||
"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",
|
||||
"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.",
|
||||
"bePatientDeleteFiles" : "Ole kärsivällinen, kun poistamme palvelimesi Crafty-paneelista ja poistamme kaikki tiedostot. Tämä näyttö sulkeutuu hetken kuluttua."
|
||||
},
|
||||
"serverConfigHelp": {
|
||||
"title": "Palvelimen asetukset",
|
||||
|
@ -16,7 +16,8 @@
|
||||
"internet": "Nous avons détecté que la machine exécutant Crafty n'a pas de connexion à Internet. Les connexions client au serveur peuvent être limitées.",
|
||||
"eulaTitle": "Accepter le EULA",
|
||||
"eulaMsg": "Vous devez accepter le EULA. Une copie du CLUF de Mojang est liée sous ce message.",
|
||||
"eulaAgree": "Êtes-vous d'accord?"
|
||||
"eulaAgree": "Êtes-vous d'accord?",
|
||||
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server."
|
||||
},
|
||||
"404": {
|
||||
"contact": "Contacter le Support de Crafty Control via Discord",
|
||||
@ -178,7 +179,10 @@
|
||||
"destroyBackup": "Supprimer la sauvegarde \" + file_to_del + \" ?",
|
||||
"confirmDelete": "Es-tu sûr de vouloir supprimer cette sauvegarde ? Tu ne pourras pas revenir en arrière.",
|
||||
"confirm": "Confirmer",
|
||||
"options": "Options"
|
||||
"options": "Options",
|
||||
"restoring": "Restauration de la sauvegarde. Cela peut prendre un peu de temps. S'il vous plaît soyez patient.",
|
||||
"restore": "Restaurer",
|
||||
"restoreConfirm": "Êtes-vous sûr de vouloir restaurer à partir de cette sauvegarde. Tous les fichiers du serveur actuel passeront à l'état de sauvegarde et seront irrécupérables."
|
||||
},
|
||||
"serverFiles": {
|
||||
"noscript": "Le gestionnaire de fichiers ne fonctionne pas sans JavaScript",
|
||||
@ -247,7 +251,7 @@
|
||||
"yesDelete": "Oui, Supprimer",
|
||||
"noDelete": "Non, revenir en arrière",
|
||||
"deleteFilesQuestion": "Supprimer les fichiers de la machine ?",
|
||||
"deleteFilesQuestionMessage": "Veux-tu que Crafty supprime tous les fichiers du serveur de la machine hôte ?",
|
||||
"deleteFilesQuestionMessage": "Veux-tu que Crafty supprime tous les fichiers du serveur de la machine hôte? <br><br><strong>Cela inclut les sauvegardes du serveur.</strong>",
|
||||
"yesDeleteFiles": "Oui, Supprimer les fichier",
|
||||
"noDeleteFiles": "Non, Supprimer uniquement du tabelau de bord",
|
||||
"sendingDelete": "Suppression du Serveur",
|
||||
|
346
app/translations/zh_CN.json
Normal file
346
app/translations/zh_CN.json
Normal file
@ -0,0 +1,346 @@
|
||||
{
|
||||
"login": {
|
||||
"forgotPassword": "忘记密码",
|
||||
"login": "登录",
|
||||
"password": "密码",
|
||||
"username": "用户名"
|
||||
},
|
||||
"error": {
|
||||
"hereIsTheError": "错误如下",
|
||||
"contact": "通过 Discord 联系 Crafty Control 支持",
|
||||
"terribleFailure": "多糟糕的错误!",
|
||||
"embarassing": "哦,天哪,这太尴尬了。",
|
||||
"error": "错误!",
|
||||
"start-error": "服务器 {} 启动失败,错误代码为:{}",
|
||||
"closedPort": "我们检测到端口 {} 在主机上可能没有打开,或者被防火墙阻断了。远程客户端到服务器的连接可能受限。",
|
||||
"internet": "我们检测到运行 Crafty 的设备没有网络连接。客户端到服务器的连接可能受限。",
|
||||
"eulaTitle": "同意最终用户许可协议(EULA)",
|
||||
"eulaMsg": "你必须同意最终用户许可协议(EULA)。一份 Mojang EULA 副本的链接在此消息下方。",
|
||||
"eulaAgree": "你同意吗?"
|
||||
},
|
||||
"404": {
|
||||
"contact": "通过 Discord 联系 Crafty Control 支持",
|
||||
"unableToFind": "我们无法找到您想要查看的页面。请再试一次,或者返回上一页并刷新。",
|
||||
"notFound": "页面未找到"
|
||||
},
|
||||
"footer": {
|
||||
"version": "版本",
|
||||
"copyright": "版权",
|
||||
"allRightsReserved": "保留所有权利"
|
||||
},
|
||||
"sidebar": {
|
||||
"dashboard": "仪表板",
|
||||
"servers": "服务器",
|
||||
"documentation": "文档",
|
||||
"credits": "鸣谢",
|
||||
"contribute": "贡献",
|
||||
"newServer": "创建新服务器",
|
||||
"navigation": "导航栏"
|
||||
},
|
||||
"serverWizard": {
|
||||
"newServer": "创建新服务器",
|
||||
"importServer": "导入现有服务器",
|
||||
"importZip": "从 Zip 文件导入",
|
||||
"serverName": "服务器名称",
|
||||
"serverPath": "服务器路径",
|
||||
"serverType": "服务器类型",
|
||||
"selectType": "选择一种类型",
|
||||
"serverVersion": "服务器版本",
|
||||
"selectVersion": "选择一个版本",
|
||||
"absoluteServerPath": "您的服务器的绝对路径",
|
||||
"serverJar": "服务器 Jar 核心",
|
||||
"minMem": "最小内存",
|
||||
"maxMem": "最大内存",
|
||||
"serverPort": "服务器端口",
|
||||
"defaultPort": "默认值为 25565",
|
||||
"sizeInGB": "大小(以 GB 为单位)",
|
||||
"zipPath": "服务器路径",
|
||||
"absoluteZipPath": "您的服务器的绝对路径",
|
||||
"resetForm": "重置表单",
|
||||
"importServerButton": "导入服务器!",
|
||||
"buildServer": "建立服务器!",
|
||||
"quickSettings": "快捷设置",
|
||||
"quickSettingsDescription": "别担心,你可以稍后再更改这些设置",
|
||||
"myNewServer": "我的新服务器",
|
||||
"bePatient": "请耐心等待,我们正在 ' + (importing ? '导入' : '下载') + ' 服务器",
|
||||
"importing": "导入服务器中……",
|
||||
"downloading": "下载服务器中……",
|
||||
"addRole": "将服务器添加到现有角色",
|
||||
"autoCreate": "如果没有选择任何角色,Crafty 将会为您创建一个!",
|
||||
"selectRole": "选择角色"
|
||||
},
|
||||
"dashboard": {
|
||||
"dashboard": "仪表板",
|
||||
"memUsage": "内存使用量",
|
||||
"cpuUsage": "CPU 使用量",
|
||||
"host": "主机",
|
||||
"players": "玩家",
|
||||
"backups": "备份",
|
||||
"newServer": "创建新服务器",
|
||||
"allServers": "所有服务器",
|
||||
"server": "服务器",
|
||||
"actions": "操作",
|
||||
"world": "世界",
|
||||
"motd": "今日消息(MOTD)",
|
||||
"version": "版本",
|
||||
"status": "状态",
|
||||
"online": "在线",
|
||||
"offline": "离线",
|
||||
"lastBackup": "上次:",
|
||||
"nextBackup": "下次:",
|
||||
"servers": "服务器",
|
||||
"cannotSeeOnMobile": "在移动设备上什么都看不到?",
|
||||
"cannotSee": "什么都看不到?",
|
||||
"cannotSeeOnMobile2": "尝试横向滚动表格。",
|
||||
"max": "最大",
|
||||
"avg": "平均",
|
||||
"bePatientStart": "请耐心等待,我们正在启动服务器。<br /> 稍后此页面会刷新",
|
||||
"bePatientStop": "请耐心等待,我们正在停止服务器。<br /> 稍后此页面会刷新",
|
||||
"bePatientRestart": "请耐心等待,我们正在重启服务器。<br /> 稍后此页面会刷新",
|
||||
"bePatientClone": "请耐心等待,我们正在克隆服务器。<br /> 稍后此页面会刷新",
|
||||
"sendingCommand": "正在发送您的指令",
|
||||
"cpuCurFreq": "当前 CPU 时钟",
|
||||
"cpuMaxFreq": "最大 CPU 时钟",
|
||||
"cpuCores": "CPU 核心",
|
||||
"start": "启动",
|
||||
"stop": "停止",
|
||||
"clone": "克隆",
|
||||
"kill": "杀死进程",
|
||||
"restart": "重启",
|
||||
"killing": "正在杀死进程……",
|
||||
"starting": "延迟启动",
|
||||
"delay-explained": "服务进程已经在刚才启动,并且正在延迟 Minecraft 服务器实例的启动",
|
||||
"no-servers": "当前没有服务器。要开始,请点击",
|
||||
"welcome": "欢迎来到 Crafty Controller"
|
||||
},
|
||||
"accessDenied": {
|
||||
"accessDenied": "拒绝访问",
|
||||
"noAccess": "您没有权限访问此资源",
|
||||
"contactAdmin": "联系您的服务器管理员来获得访问此资源的权限,或者如果您认为您应该有权限访问此资源,请联系支持。",
|
||||
"contact": "通过 Discord 联系 Crafty Control 支持"
|
||||
},
|
||||
"serverStats": {
|
||||
"online": "运行中",
|
||||
"offline": "已停止",
|
||||
"serverStatus": "服务器状态",
|
||||
"serverStarted": "服务器已启动",
|
||||
"serverUptime": "服务器正常运行时间",
|
||||
"players": "玩家",
|
||||
"memUsage": "内存使用量",
|
||||
"cpuUsage": "CPU 使用量",
|
||||
"version": "版本",
|
||||
"description": "简介",
|
||||
"errorCalculatingUptime": "计算正常运行时间时发生错误",
|
||||
"serverTime": "UTC 时间",
|
||||
"unableToConnect": "无法连接"
|
||||
},
|
||||
"serverDetails": {
|
||||
"serverDetails": "服务器详情",
|
||||
"terminal": "终端",
|
||||
"logs": "日志",
|
||||
"schedule": "计划",
|
||||
"backup": "备份",
|
||||
"files": "文件",
|
||||
"config": "配置",
|
||||
"playerControls": "玩家管理"
|
||||
},
|
||||
"serverTerm": {
|
||||
"stopScroll": "停止自动滚动",
|
||||
"commandInput": "输入您的指令",
|
||||
"sendCommand": "发送指令",
|
||||
"start": "启动",
|
||||
"restart": "重启",
|
||||
"stop": "停止",
|
||||
"updating": "更新中……",
|
||||
"starting": "延迟启动",
|
||||
"delay-explained": "服务进程已经在刚才启动,并且正在延迟 Minecraft 服务器实例的启动"
|
||||
},
|
||||
"serverPlayerManagement": {
|
||||
"players": "玩家",
|
||||
"bannedPlayers": "已封禁的玩家",
|
||||
"loadingBannedPlayers": "正在加载已封禁的玩家"
|
||||
},
|
||||
"serverBackups": {
|
||||
"backupNow": "现在备份!",
|
||||
"backupAtMidnight": "午夜自动备份?",
|
||||
"storageLocation": "存储位置",
|
||||
"storageLocationDesc": "您想要在哪里存储备份?",
|
||||
"maxBackups": "最大备份数量",
|
||||
"maxBackupsDesc": "Crafty 不会存储多于 N 个备份,并且会删除最旧的备份(输入 0 以保留所有备份)",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"currentBackups": "现有备份",
|
||||
"download": "下载",
|
||||
"path": "路径",
|
||||
"size": "大小",
|
||||
"delete": "删除",
|
||||
"backupTask": "一个备份任务已开始。",
|
||||
"destroyBackup": "删除备份 \" + file_to_del + \"?",
|
||||
"confirmDelete": "您想要删除这个备份吗?此操作不能撤销。",
|
||||
"confirm": "确认",
|
||||
"options": "选项"
|
||||
},
|
||||
"serverFiles": {
|
||||
"noscript": "文件管理器无法在没有 JavaScript 的情况下使用",
|
||||
"error": "获取文件时发生错误",
|
||||
"files": "文件",
|
||||
"default": "默认",
|
||||
"save": "保存",
|
||||
"editingFile": "正在编辑文件",
|
||||
"delete": "删除",
|
||||
"createFile": "创建文件",
|
||||
"createDir": "创建目录",
|
||||
"rename": "重命名",
|
||||
"createFileQuestion": "您希望新文件叫什么名字?",
|
||||
"createDirQuestion": "您希望新目录叫什么名字?",
|
||||
"renameItemQuestion": "新名称应当是什么?",
|
||||
"deleteItemQuestion": "您确定要删除 \" + name + \" 吗?",
|
||||
"deleteItemQuestionMessage": "您正在删除 \\\"\" + path + \"\\\"!<br/><br/>此操作不可逆转,文件将永远遗失!",
|
||||
"yesDelete": "是,我知道结果",
|
||||
"noDelete": "否",
|
||||
"unsupportedLanguage": "警告:这不是一个受支持的文件类型",
|
||||
"keybindings": "按键绑定",
|
||||
"fileReadError": "文件读取错误",
|
||||
"upload": "上传",
|
||||
"unzip": "解压",
|
||||
"clickUpload": "点击这里来选择您的文件",
|
||||
"uploadTitle": "上传文件到:",
|
||||
"waitUpload": "请等待,我们正在上传您的文件……这需要一点时间。",
|
||||
"stayHere": "请不要离开此页面!",
|
||||
"close": "关闭",
|
||||
"download": "下载"
|
||||
},
|
||||
"serverConfig": {
|
||||
"serverName": "服务器名称",
|
||||
"serverNameDesc": "您希望把这个服务器叫做什么",
|
||||
"serverPath": "服务器运行目录",
|
||||
"serverPathDesc": "完整绝对路径(不包含可执行文件)",
|
||||
"serverLogLocation": "服务器日志路径",
|
||||
"serverLogLocationDesc": "日志文件的完整绝对路径",
|
||||
"serverExecutable": "服务器可执行文件",
|
||||
"serverExecutableDesc": "服务器的可执行文件",
|
||||
"serverExecutionCommand": "服务器运行命令",
|
||||
"serverExecutionCommandDesc": "在隐藏的终端内要如何启动服务器",
|
||||
"serverStopCommand": "服务器停止指令",
|
||||
"serverStopCommandDesc": "要发送给程序以关闭它的指令",
|
||||
"serverAutostartDelay": "服务器自动启动延迟",
|
||||
"serverAutostartDelayDesc": "自动启动前的延迟(如果已在下方启用)",
|
||||
"serverIP": "服务器 IP",
|
||||
"serverIPDesc": "Crafty 要连接以获取状态的 IP(如果遇到问题,尝试使用真实 IP 而非 127.0.0.1)",
|
||||
"serverPort": "服务器端口",
|
||||
"serverPortDesc": "Crafty 要连接以获取状态的端口",
|
||||
"removeOldLogsAfter": "此时间后删除旧日志",
|
||||
"removeOldLogsAfterDesc": "日志文件要在多少天后视为旧文件被删除(0 为关闭)",
|
||||
"serverAutoStart": "服务器自动启动",
|
||||
"serverCrashDetection": "服务器崩溃检测",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"deleteServer": "删除服务器",
|
||||
"stopBeforeDeleting": "请在删除之前停止服务器",
|
||||
"exeUpdateURLDesc": "用于下载更新的直接链接。",
|
||||
"exeUpdateURL": "服务器可执行文件更新地址",
|
||||
"update": "更新可执行文件",
|
||||
"bePatientUpdate": "请耐心等待,我们正在更新服务器。下载时长可能因您的网络速度而异。<br /> 稍后此页面会刷新",
|
||||
"sendingRequest": "正在发送您的请求……",
|
||||
"deleteServerQuestion": "删除服务器?",
|
||||
"deleteServerQuestionMessage": "您确定要删除此服务器吗?在此之后将无法撤销……",
|
||||
"yesDelete": "是,删除",
|
||||
"noDelete": "否,返回",
|
||||
"deleteFilesQuestion": "从设备上删除服务器文件?",
|
||||
"deleteFilesQuestionMessage": "您想要 Crafty 从主机上删除所有的服务器文件吗?",
|
||||
"yesDeleteFiles": "是,删除文件",
|
||||
"noDeleteFiles": "否,只从面板中移除",
|
||||
"sendingDelete": "正在删除服务器",
|
||||
"bePatientDelete": "请耐心等待,我们正在从 Crafty 面板中移除服务器。稍后此页面会关闭。",
|
||||
"bePatientDeleteFiles" : "请耐心等待,我们正在从 Crafty 面板中移除服务器并删除所有文件。稍后此页面会关闭。"
|
||||
},
|
||||
"serverConfigHelp": {
|
||||
"title": "服务器配置区",
|
||||
"desc": "您可以在这里更改您的服务器配置",
|
||||
"perms": [
|
||||
"我们<code>不推荐</code>更改由 Crafty 管理的服务器的路径。",
|
||||
"更改路径<code>可能会</code>破坏一些东西,尤其是在 Linux 这类文件权限锁定得更加严格的操作系统上。",
|
||||
"<br /><br/>",
|
||||
"如果您认为您必须更改服务器存放的位置,你可能需要给予 \"crafty\" 用户对服务器路径的读取/写入权限。",
|
||||
"<br />",
|
||||
"<br />",
|
||||
"在 Linux 上,最好通过执行如下命令来完成:<br />",
|
||||
"<code>",
|
||||
" sudo chown crafty:crafty /您的/服务器/路径 -R<br />",
|
||||
" sudo chmod 2775 /您的/服务器/路径 -R<br />",
|
||||
"</code>"
|
||||
]
|
||||
},
|
||||
"panelConfig": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除"
|
||||
},
|
||||
"datatables": {
|
||||
"i18n": {
|
||||
"decimal": "",
|
||||
"emptyTable": "数据表中没有可用的数据",
|
||||
"info": "正在显示从 _START_ 到 _END_ 的共 _TOTAL_ 个项目",
|
||||
"infoEmpty": "正在显示从 0 到 0 的共 0 个项目",
|
||||
"infoFiltered": "(从 _MAX_ 个项目中筛选出)",
|
||||
"infoPostFix": "",
|
||||
"thousands": ",",
|
||||
"lengthMenu": "显示 _MENU_ 个项目",
|
||||
"loadingRecords": "正在加载……",
|
||||
"processing": "正在处理……",
|
||||
"search": "搜索:",
|
||||
"zeroRecords": "没有找到匹配的记录",
|
||||
"paginate": {
|
||||
"first": "首页",
|
||||
"last": "末页",
|
||||
"next": "下一页",
|
||||
"previous": "上一页"
|
||||
},
|
||||
"aria": {
|
||||
"sortAscending": ":激活对队列的升序排列",
|
||||
"sortDescending": ":激活对队列的降序排列"
|
||||
},
|
||||
"buttons": {
|
||||
"collection": "合集 <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'\/>",
|
||||
"colvis": "列可见性",
|
||||
"colvisRestore": "恢复可见性",
|
||||
"copy": "复制",
|
||||
"copyKeys": "按 ctrl 或 u2318 + C 以复制表中的数据到您的系统剪贴板。<br><br>点击这条消息或者按 escape(ESC)来取消。",
|
||||
"copySuccess": {
|
||||
"1": "复制了 1 行到剪贴板",
|
||||
"_": "复制了 %d 行到剪贴板"
|
||||
},
|
||||
"copyTitle": "复制到剪贴板",
|
||||
"csv": "CSV",
|
||||
"excel": "Excel",
|
||||
"pageLength": {
|
||||
"-1": "显示所有行",
|
||||
"1": "显示 1 行",
|
||||
"_": "显示 %d 行"
|
||||
},
|
||||
"pdf": "PDF",
|
||||
"print": "打印"
|
||||
},
|
||||
"select": {
|
||||
"rows": {
|
||||
"0": "点击某一行以选择",
|
||||
"1": "%d 行已选中",
|
||||
"_": "%d 行已选中"
|
||||
},
|
||||
"cells": {
|
||||
"0": "点击某个单元格以选择",
|
||||
"1": "%d 个单元格已选中",
|
||||
"_": "%d 个单元格已选中"
|
||||
},
|
||||
"columns": {
|
||||
"0": "点击某一列以选择",
|
||||
"1": "%d 列已选中",
|
||||
"_": "%d 列已选中"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>警告:</strong>Crafty 无法在没有 JavaScript 的情况下使用!"
|
||||
}
|
||||
}
|
11
main.py
11
main.py
@ -1,3 +1,4 @@
|
||||
from cmd import Cmd
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
@ -78,13 +79,21 @@ if __name__ == '__main__':
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if helper.check_file_exists('/.dockerenv'):
|
||||
console.cyan("Docker environment detected!")
|
||||
else:
|
||||
if helper.checkRoot():
|
||||
console.critical("Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions.")
|
||||
time.sleep(5)
|
||||
console.critical("Crafty shutting down. Root/Admin access denied.")
|
||||
sys.exit(0)
|
||||
helper.ensure_logging_setup()
|
||||
|
||||
setup_logging(debug=args.verbose)
|
||||
|
||||
# setting up the logger object
|
||||
logger = logging.getLogger(__name__)
|
||||
print("Logging set to: {} ".format(logger.level))
|
||||
console.cyan("Logging set to: {} ".format(logger.level))
|
||||
|
||||
# print our pretty start message
|
||||
do_intro()
|
||||
|
@ -1,7 +1,7 @@
|
||||
cryptography~=3.4
|
||||
argon2-cffi~=20.1
|
||||
bleach~=3.1
|
||||
colorama~=0.4
|
||||
cryptography~=3.4
|
||||
peewee~=3.13
|
||||
pexpect~=4.8
|
||||
psutil~=5.7
|
||||
@ -11,3 +11,4 @@ requests~=2.26
|
||||
schedule~=1.1.0
|
||||
termcolor~=1.1
|
||||
tornado~=6.0
|
||||
cached_property==1.5.2
|
Loading…
Reference in New Issue
Block a user