Improved File Loading, Fixed Port checking

This commit is contained in:
Andrew 2022-01-08 23:03:45 +00:00
parent abb7b8af08
commit 7dd24b6b15
21 changed files with 307 additions and 89 deletions

View File

@ -94,8 +94,8 @@ class Users_Controller:
users_helper.update_user(user_id, up_data)
@staticmethod
def add_user(username, password=None, api_token=None, enabled=True, superuser=False):
return users_helper.add_user(username, password=password, api_token=api_token, enabled=enabled, superuser=superuser)
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)
@staticmethod
def remove_user(user_id):

View File

@ -38,6 +38,7 @@ class Users(Model):
last_ip = CharField(default="")
username = CharField(default="", unique=True, index=True)
password = CharField(default="")
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
@ -112,6 +113,7 @@ class helper_users:
'last_ip': "127.27.23.89",
'username': "SYSTEM",
'password': None,
'email': "default@example.com",
'enabled': True,
'superuser': True,
'api_token': None,
@ -136,7 +138,7 @@ class helper_users:
return False
@staticmethod
def add_user(username, password=None, api_token=None, enabled=True, superuser=False):
def add_user(username, password=None, email=None, api_token=None, enabled=True, superuser=False):
if password is not None:
pw_enc = helper.encode_pass(password)
else:
@ -149,6 +151,7 @@ class helper_users:
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,

View File

@ -97,13 +97,33 @@ class Helpers:
@staticmethod
def check_port(server_port):
try:
ip = get('https://api.ipify.org').content.decode('utf8')
except:
ip = 'google.com'
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = get('https://api.ipify.org').content.decode('utf8')
a_socket.settimeout(20.0)
location = (ip, server_port)
result_of_check = a_socket.connect_ex(location)
a_socket.close()
if result_of_check == 0:
return True
else:
return False
@staticmethod
def check_server_conn(server_port):
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
a_socket.settimeout(10.0)
ip = '127.0.0.1'
location = (ip, server_port)
result_of_check = a_socket.connect_ex(location)
a_socket.close()
if result_of_check == 0:
return True
else:
@ -654,29 +674,58 @@ class Helpers:
@staticmethod
def generate_tree(folder, output=""):
file_list = os.listdir(folder)
file_list.sort()
file_list = sorted(file_list, key=str.casefold)
for raw_filename in file_list:
filename = html.escape(raw_filename)
rel = os.path.join(folder, raw_filename)
if os.path.isdir(rel):
output += \
"""<li class="tree-item" data-path="{}">
\n<div data-path="{}" data-name="{}" class="tree-caret tree-ctx-item tree-folder">
\n<div id="{}" data-path="{}" data-name="{}" class="tree-caret tree-ctx-item tree-folder">
<span id="{}span" class="files-tree-title" data-path="{}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{}
</div>
\n<ul class="tree-nested">"""\
.format(os.path.join(folder, filename), os.path.join(folder, filename), filename, filename)
</span>
</div><li>
\n"""\
.format(os.path.join(folder, filename), os.path.join(folder, filename), os.path.join(folder, filename), filename, os.path.join(folder, filename), os.path.join(folder, filename), filename)
else:
output += """<li
class="tree-item tree-ctx-item tree-file"
data-path="{}"
data-name="{}"
onclick="clickOnFile(event)"><span style="margin-right: 6px;"><i class="far fa-file"></i></span>{}</li>""".format(os.path.join(folder, filename), filename, filename)
return output
output += helper.generate_tree(rel)
output += '</ul>\n</li>'
@staticmethod
def generate_dir(folder, output=""):
file_list = os.listdir(folder)
file_list = sorted(file_list, key=str.casefold)
output += \
"""<ul class="tree-nested d-block" id="{}ul">"""\
.format(folder)
for raw_filename in file_list:
filename = html.escape(raw_filename)
rel = os.path.join(folder, raw_filename)
if os.path.isdir(rel):
output += \
"""<li class="tree-item" data-path="{}">
\n<div id="{}" data-path="{}" data-name="{}" class="tree-caret tree-ctx-item tree-folder">
<span id="{}span" class="files-tree-title" data-path="{}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{}
</span>
</div><li>"""\
.format(os.path.join(folder, filename), os.path.join(folder, filename), os.path.join(folder, filename), filename, os.path.join(folder, filename), os.path.join(folder, filename), filename)
else:
output += """<li
class="tree-item tree-ctx-item tree-file"
data-path="{}"
data-name="{}"
onclick="clickOnFile(event)"><span style="margin-right: 6px;"><i class="far fa-file"></i></span>{}</li>""".format(os.path.join(folder, filename), filename, filename)
output += '</ul>\n'
return output
@staticmethod

View File

@ -130,7 +130,7 @@ class Controller:
@staticmethod
def add_system_user():
helper_users.add_user("system", helper.random_string_generator(64), helper_users.new_api_token(), False, False)
helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", helper_users.new_api_token(), False, False)
def get_server_settings(self, server_id):
for s in self.servers_list:

View File

@ -48,7 +48,7 @@ class db_builder:
# Users.enabled: True,
# Users.superuser: True
#}).execute()
user_id = users_helper.add_user(username=username, password=password, superuser=True)
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))

View File

@ -274,6 +274,8 @@ class Server:
self.stats.record_stats()
websocket_helper.broadcast_user(user_id, 'send_start_reload', {
})
check_port_thread = threading.Thread(target=self.check_internet_thread, daemon=True, args=(user_id, user_lang, ), name=f"backup_{self.name}")
check_port_thread.start()
else:
logger.warning("Server PID {} died right after starting - is this a server config issue?".format(self.process.pid))
console.warning("Server PID {} died right after starting - is this a server config issue?".format(self.process.pid))
@ -284,27 +286,26 @@ class Server:
self.crash_watcher_schedule = schedule.every(30).seconds.do(self.detect_crash).tag(self.name)
check_port_thread = threading.Thread(target=self.check_internet_thread, daemon=True, args=(user_id, user_lang, ), name=f"backup_{self.name}")
check_port_thread.start()
def check_internet_thread(self, user_id, user_lang):
if user_id:
if helper.check_internet():
loc_server_port = servers_helper.get_server_stats_by_id(self.server_id)['server_port']
port_status = False
for i in range(3):
if helper.check_port(loc_server_port):
port_status = True
return
checked = False
while not checked:
if helper.check_server_conn(loc_server_port):
checked = True
result_of_check = helper.check_port(loc_server_port)
if result_of_check == True:
return
else:
websocket_helper.broadcast_user(user_id, 'send_start_error', {
'error': translation.translate('error', 'closedPort', user_lang).format(loc_server_port)
})
else:
time.sleep(5)
if port_status == False:
websocket_helper.broadcast_user(user_id, 'send_start_error', {
'error': translation.translate('error', 'closedPort', user_lang).format(loc_server_port)
})
else:
websocket_helper.broadcast_user(user_id, 'send_start_error', {
'error': translation.translate('error', 'internet', user_lang)

View File

@ -124,12 +124,26 @@ class AjaxHandler(BaseHandler):
elif page == "get_tree":
server_id = self.get_argument('id', None)
path = self.get_argument('path', None)
if not self.check_server_id(server_id, 'get_tree'): return
else: server_id = bleach.clean(server_id)
self.write(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']) + '\n' +
helper.generate_tree(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path'])))
if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path):
self.write(helper.get_os_understandable_path(path) + '\n' +
helper.generate_tree(path))
self.finish()
elif page == "get_dir":
server_id = self.get_argument('id', None)
path = self.get_argument('path', None)
if not self.check_server_id(server_id, 'get_tree'): return
else: server_id = bleach.clean(server_id)
if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path):
self.write(helper.get_os_understandable_path(path) + '\n' +
helper.generate_dir(path))
self.finish()
@tornado.web.authenticated
@ -328,7 +342,8 @@ class AjaxHandler(BaseHandler):
return
# Delete the file
os.remove(file_path)
if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), file_path):
os.remove(file_path)
elif page == "del_dir":
if not permissions['Files'] in user_perms:
@ -352,7 +367,8 @@ class AjaxHandler(BaseHandler):
# Delete the directory
# os.rmdir(dir_path) # Would only remove empty directories
shutil.rmtree(dir_path) # Removes also when there are contents
if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), dir_path):
shutil.rmtree(dir_path) # Removes also when there are contents
elif page == "delete_server":
if not permissions['Config'] in user_perms:

View File

@ -422,6 +422,7 @@ class PanelHandler(BaseHandler):
page_data['user'] = {}
page_data['user']['username'] = ""
page_data['user']['user_id'] = -1
page_data['user']['email'] = ""
page_data['user']['enabled'] = True
page_data['user']['superuser'] = False
page_data['user']['api_token'] = "N/A"
@ -489,6 +490,9 @@ class PanelHandler(BaseHandler):
if exec_user['user_id'] != page_data['user']['user_id']:
page_data['user']['api_token'] = "********"
if exec_user['email'] == 'default@example.com':
page_data['user']['email'] = ""
template = "panel/panel_edit_user.html"
elif page == "remove_user":
@ -824,6 +828,7 @@ class PanelHandler(BaseHandler):
username = bleach.clean(self.get_argument('username', None))
password0 = bleach.clean(self.get_argument('password0', None))
password1 = bleach.clean(self.get_argument('password1', None))
email = bleach.clean(self.get_argument('email', "default@example.com"))
enabled = int(float(self.get_argument('enabled', '0')))
regen_api = int(float(self.get_argument('regen_api', '0')))
lang = bleach.clean(self.get_argument('language'), 'en_EN')
@ -894,9 +899,13 @@ class PanelHandler(BaseHandler):
else:
server_quantity[permission.name] = 0
# if email is None or "":
# email = "default@example.com"
user_data = {
"username": username,
"password": password0,
"email": email,
"enabled": enabled,
"regen_api": regen_api,
"roles": roles,
@ -922,6 +931,7 @@ class PanelHandler(BaseHandler):
username = bleach.clean(self.get_argument('username', None))
password0 = bleach.clean(self.get_argument('password0', None))
password1 = bleach.clean(self.get_argument('password1', None))
email = bleach.clean(self.get_argument('email', "default@example.com"))
enabled = int(float(self.get_argument('enabled', '0'))),
lang = bleach.clean(self.get_argument('lang', 'en_EN'))
@ -972,7 +982,7 @@ class PanelHandler(BaseHandler):
else:
server_quantity[permission.name] = 0
user_id = self.controller.users.add_user(username, password=password0, enabled=enabled)
user_id = self.controller.users.add_user(username, password=password0, email=email, enabled=enabled)
user_data = {
"roles": roles,
'lang': lang

View File

@ -1,10 +1,13 @@
from re import X
import sys
import json
import libgravatar
import logging
import requests
import tornado.web
import tornado.escape
from app.classes.shared.helpers import helper
from app.classes.shared.helpers import Helpers, helper
from app.classes.web.base_handler import BaseHandler
from app.classes.shared.console import console
from app.classes.shared.main_models import fn
@ -121,9 +124,27 @@ 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,
}

View File

@ -12,5 +12,6 @@
"show_contribute_link": true,
"virtual_terminal_lines": 70,
"max_log_lines": 700,
"keywords": ["help", "chunk"]
"keywords": ["help", "chunk"],
"allow_nsfw_profile_pictures": false
}

View File

@ -29,6 +29,7 @@
<link rel="shortcut icon" href="/static/assets/images/favicon.png" />
<link rel="stylesheet" href="/static/assets/css/crafty.css">
</head>
<body class="dark-theme">

View File

@ -2,7 +2,11 @@
<footer class="footer">
<div class="container-fluid ">
<<<<<<< app/frontend/templates/footer.html
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">{{ translate('footer', 'copyright', data['lang']) }} © 2021 - 2022 <a href="https://craftycontrol.com/" target="_blank">Crafty Controller</a>. {{ translate('footer', 'allRightsReserved', data['lang']) }}.</span>
=======
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">{{ translate('footer', 'copyright', data['lang']) }} © 2021 - 2022 <a href="http://www.craftycontrol.com/" target="_blank">Crafty Controller</a>. {{ translate('footer', 'allRightsReserved', data['lang']) }}.</span>
>>>>>>> app/frontend/templates/footer.html
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">{{ translate('footer', 'version', data['lang']) }}: {{ data['version_data'] }}
</span>
</div>

View File

@ -18,15 +18,16 @@
<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" src="/static/assets/images/faces-clipart/pic-1.png" alt="Profile image"> </a>
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" 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" src="/static/assets/images/faces-clipart/pic-1.png" alt="Profile image">
<img class="img-md rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" 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 %}
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
</div>
{% 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>

View File

@ -80,6 +80,10 @@
<label class="form-label" for="password1">Repeat Password <small class="text-muted ml-1"> - leave blank to don't change</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>
<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>
<select class="form-select form-control form-control-lg select-css" id="language" name="language" form="user_form">

View File

@ -25,7 +25,7 @@
</div>
<!-- Page Title Header Ends-->
{% include "parts/details_stats.html %}
{% include "parts/details_stats.html" %}
<div class="row">

View File

@ -25,7 +25,7 @@
</div>
<!-- Page Title Header Ends-->
{% include "parts/details_stats.html %}
{% include "parts/details_stats.html" %}
<div class="row">
@ -134,13 +134,15 @@
</style>
<ul class="tree-view">
<li>
<div class="tree-caret tree-ctx-item files-tree-title">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{{ translate('serverFiles', 'files', data['lang']) }}
<div class="tree-ctx-item" data-path="{{ data['server_stats']['server_id']['path'] }}">
<span id="{{ data['server_stats']['server_id']['path'] }}span" class="files-tree-title tree-caret-down" data-path="{{ data['server_stats']['server_id']['path'] }}" onclick="getToggleMain(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{{ translate('serverFiles', 'files', data['lang']) }}
</span>
</div>
<ul class="tree-nested d-block" id="files-tree">
<li>{{ translate('serverFiles', 'error', data['lang']) }}</li>
<li><i class="fa fa-spin fa-spinner"></i>{{ translate('serverFiles', 'loadingRecords', data['lang']) }}</li>
</ul>
</li>
@ -648,10 +650,13 @@
}, false);
});
}
function getTreeView() {
function getTreeView(event) {
path = '{{ data['server_stats']['server_id']['path'] }}'
$.ajax({
type: "GET",
url: '/ajax/get_tree?id={{ data['server_stats']['server_id']['server_id'] }}',
url: '/ajax/get_tree?id={{ data['server_stats']['server_id']['server_id'] }}&path='+path,
dataType: 'text',
success: function(data){
console.log("got response:");
@ -661,26 +666,75 @@
serverDir = dataArr.shift(); // Remove & return first element (server directory)
text = dataArr.join('\n');
document.getElementById('files-tree').innerHTML = text;
try{
document.getElementById(path).innerHTML += text;
event.target.parentElement.classList.add("clicked");
}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');
setTimeout(function () {setTreeViewContext()}, 1000);
},
});
}
var toggler = document.getElementsByClassName("tree-caret");
var i;
function getToggleMain(event) {
path = event.target.parentElement.getAttribute('data-path');
document.getElementById("files-tree").classList.toggle("d-block");
document.getElementById(path+"span").classList.toggle("tree-caret-down");
document.getElementById(path+"span").classList.toggle("tree-caret");
}
for (i = 0; i < toggler.length; i++) {
if (toggler[i].classList.contains('files-tree-title')) continue;
toggler[i].addEventListener("click", function caretListener() {
this.parentElement.querySelector(".tree-nested").classList.toggle("d-block");
this.classList.toggle("tree-caret-down");
function getDirView(event) {
path = event.target.parentElement.getAttribute('data-path');
if (document.getElementById(path).classList.contains('clicked')){
var toggler = document.getElementById(path+"span");
if (toggler.classList.contains('files-tree-title')){
document.getElementById(path+"ul").classList.toggle("d-block");
document.getElementById(path+"span").classList.toggle("tree-caret-down");
}
return;
}else{
$.ajax({
type: "GET",
url: '/ajax/get_dir?id={{ data['server_stats']['server_id']['server_id'] }}&path='+path,
dataType: 'text',
success: function(data){
console.log("got response:");
dataArr = data.split('\n');
serverDir = dataArr.shift(); // Remove & return first element (server directory)
text = dataArr.join('\n');
try{
document.getElementById(path+"span").classList.add('tree-caret-down');
document.getElementById(path).innerHTML += text;
document.getElementById(path).classList.add("clicked");
}catch{
console.log("Bad")
}
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");
document.getElementById(path+"span").classList.toggle("tree-caret-down");
});
}
},
});
}
}
function setTreeViewContext() {
var treeItems = document.getElementsByClassName('tree-ctx-item');
@ -875,11 +929,6 @@
});
}
document.getElementsByClassName('files-tree-title')[0].addEventListener("click", function caretListener() {
this.parentElement.querySelector(".tree-nested").classList.toggle("d-block");
this.classList.toggle("tree-caret-down");
});
getTreeView();
setTreeViewContext();
@ -898,7 +947,7 @@
target.classList.add('btn-primary');
}
window.onload = getTreeView();
</script>

View File

@ -43,36 +43,51 @@
<div class="form-group">
<label for="server_name">Action<small class="text-muted ml-1"></small> </label><br>
<select id="action" name="action" onchange="yesnoCheck(this);" class="form-control form-control-lg select-css">
<option value="start">Start Server</option>
<option value="restart">Restart Server</option>
<option value="shutdown">Shutdown Server</option>
<option value="command">Custon Command</option>
<select id="action" name="action" onchange="basicAdvanced(this);" class="form-control form-control-lg select-css">
<option value="basic">Basic</option>
<option value="advanced">Advanced</option>
</select>
</div>
<div class="form-group">
<label for="server_path">Interval <small class="text-muted ml-1"> - How often you want this task to execute</small> </label>
<input type="number" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="Interval" required>
<br>
<br>
<select id="interval_type" name="interval_type" class="form-control form-control-lg select-css">
<option value="days">Days</option>
<option value="hours">Hours</option>
<option value="minutes">Minutes</option>
<option value="weeks">Weeks</option>
</select>
</div>
<div class="form-group">
<label for="time">Time <small class="text-muted ml-1"> - What time do you want your task to execute?</small> </label>
<input type="time" class="form-control" name="time" id="time" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="Time" required>
</div>
<div id="ifYes" style="display: none;">
<div id="ifBasic">
<div class="form-group">
<label for="command">Command <small class="text-muted ml-1"> - What command do you want us to execute? Do not include the '/'</small> </label>
<input type="input" class="form-control" name="command" id="command" value="" placeholder="Command" required>
<label for="server_name">Action<small class="text-muted ml-1"></small> </label><br>
<select id="action" name="action" onchange="yesnoCheck(this);" class="form-control form-control-lg select-css">
<option value="start">Start Server</option>
<option value="restart">Restart Server</option>
<option value="shutdown">Shutdown Server</option>
<option value="command">Custon Command</option>
</select>
</div>
<div class="form-group">
<label for="server_path">Interval <small class="text-muted ml-1"> - How often you want this task to execute</small> </label>
<input type="number" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="Interval" required>
<br>
<br>
<select id="interval_type" name="interval_type" class="form-control form-control-lg select-css">
<option value="days">Days</option>
<option value="hours">Hours</option>
<option value="minutes">Minutes</option>
<option value="weeks">Weeks</option>
</select>
</div>
<div class="form-group">
<label for="time">Time <small class="text-muted ml-1"> - What time do you want your task to execute?</small> </label>
<input type="time" class="form-control" name="time" id="time" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="Time" required>
</div>
<div id="ifYes" style="display: none;">
<div class="form-group">
<label for="command">Command <small class="text-muted ml-1"> - What command do you want us to execute? Do not include the '/'</small> </label>
<input type="input" class="form-control" name="command" id="command" value="" placeholder="Command" required>
</div>
</div>
</div>
<div id="ifAdvanced" style="display: none;">
<div class="form-group">
<label for="cron">Cron <small class="text-muted ml-1"> - Input your cron string</small> </label>
<input type="input" class="form-control" name="cron" id="cron" value="* * * * backup_server" placeholder="Cron" required>
</div>
</div>
<div class="form-check-flat">
@ -173,6 +188,15 @@
document.getElementById("ifYes").style.display = "none";
}
}
function basicAdvanced(that) {
if (that.value == "advanced") {
document.getElementById("ifAdvanced").style.display = "block";
document.getElementById("ifBasic").style.display = "none";
} else {
document.getElementById("ifAdvanced").style.display = "none";
document.getElementById("ifBasic").style.display = "block";
}
}
</script>

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns('users', email=peewee.CharField(default="default@example.com"))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns('users', ['email'])
"""
Write your rollback migrations here.
"""

View File

@ -212,7 +212,8 @@
"waitUpload": "Please wait while we upload your files... This may take a while.",
"stayHere": "DO NOT LEAVE THIS PAGE!",
"close": "Close",
"download": "Download"
"download": "Download",
"loadingRecords": "Loading Files..."
},
"serverConfig": {
"serverName": "Server Name",

22
main.py
View File

@ -6,6 +6,7 @@ import time
import argparse
import logging.config
import signal
import threading
from app.classes.controllers.management_controller import Management_Controller
""" Our custom classes / pip packages """
@ -36,6 +37,22 @@ def do_intro():
console.magenta(intro)
def check_port_thread():
port = helper.get_setting('https_port')
checked = False
while not checked:
if helper.check_server_conn(port):
checked = True
result_of_check = helper.check_port(port)
if result_of_check == True:
return
else:
console.warning("We have detected Crafty's port, {} may not be open on the host network or a firewall is blocking it. Remote client connections to Crafty may be limited.".format(helper.get_setting('https_port')))
console.help("If you are not forwarding ports from your public IP or your router does not support hairpin NAT you can safely disregard the previous message.")
else:
time.sleep(5)
return
def setup_logging(debug=True):
logging_config_file = os.path.join(os.path.curdir,
@ -143,9 +160,8 @@ if __name__ == '__main__':
if not helper.check_internet():
console.warning("We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.")
elif not helper.check_port(helper.get_setting('https_port')):
console.warning("We have detected Crafty's port, {} may not be open on the host network or a firewall is blocking it. Remote client connections to Crafty may be limited.".format(helper.get_setting('https_port')))
console.help("If you are not forwarding ports from your public IP or your router does not support hairpin NAT you can safely disregard the previous message.")
check_port_thread = threading.Thread(target=check_port_thread, daemon=True, name="crafty_port_check")
check_port_thread.start()
if not controller.check_system_user():
controller.add_system_user()

View File

@ -3,6 +3,7 @@ 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