mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into 'websocket'
# Conflicts: # app/classes/shared/models.py # app/classes/shared/tasks.py
This commit is contained in:
commit
85403c61af
@ -25,13 +25,19 @@ except ModuleNotFoundError as e:
|
||||
|
||||
class ServerJars:
|
||||
|
||||
def __init__(self):
|
||||
self.base_url = "https://serverjars.com"
|
||||
|
||||
def _get_api_result(self, call_url: str):
|
||||
base_url = "https://serverjars.com"
|
||||
full_url = "{base}{call_url}".format(base=base_url, call_url=call_url)
|
||||
full_url = "{base}{call_url}".format(base=self.base_url, call_url=call_url)
|
||||
|
||||
r = requests.get(full_url, timeout=2)
|
||||
try:
|
||||
r = requests.get(full_url, timeout=2)
|
||||
|
||||
if r.status_code not in [200, 201]:
|
||||
if r.status_code not in [200, 201]:
|
||||
return {}
|
||||
except Exception as e:
|
||||
logger.error("Unable to connect to serverjar.com api due to error: {}".format(e))
|
||||
return {}
|
||||
|
||||
try:
|
||||
@ -66,18 +72,21 @@ class ServerJars:
|
||||
data = self._read_cache()
|
||||
return data.get('servers')
|
||||
|
||||
@staticmethod
|
||||
def _check_api_alive():
|
||||
def _check_api_alive(self):
|
||||
logger.info("Checking serverjars.com API status")
|
||||
|
||||
check_url = "https://serverjars.com/api/fetchTypes"
|
||||
r = requests.get(check_url, timeout=2)
|
||||
check_url = "{base}/api/fetchTypes".format(base=self.base_url)
|
||||
try:
|
||||
r = requests.get(check_url, timeout=2)
|
||||
|
||||
if r.status_code in [200, 201]:
|
||||
logger.info("Serverjars.com API is alive")
|
||||
return True
|
||||
if r.status_code in [200, 201]:
|
||||
logger.info("Serverjars.com API is alive")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error("Unable to connect to serverjar.com api due to error: {}".format(e))
|
||||
return {}
|
||||
|
||||
logger.error("unable to contact Serverjars.com api")
|
||||
logger.error("unable to contact serverjars.com api")
|
||||
return False
|
||||
|
||||
def refresh_cache(self):
|
||||
@ -141,12 +150,11 @@ class ServerJars:
|
||||
response = self._get_api_result(url)
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def download_jar(server, version, path):
|
||||
base_url = "https://serverjars.com/api/fetchJar/{server}/{version}".format(server=server, version=version)
|
||||
def download_jar(self, server, version, path):
|
||||
fetch_url = "{base}/api/fetchJar/{server}/{version}".format(base=self.base_url, server=server, version=version)
|
||||
|
||||
# open a file stream
|
||||
with requests.get(base_url, timeout=2, stream=True) as r:
|
||||
with requests.get(fetch_url, timeout=2, stream=True) as r:
|
||||
try:
|
||||
with open(path, 'wb') as output:
|
||||
shutil.copyfileobj(r.raw, output)
|
||||
|
@ -19,12 +19,16 @@ class Stats:
|
||||
def get_node_stats(self):
|
||||
boot_time = datetime.datetime.fromtimestamp(psutil.boot_time())
|
||||
data = {}
|
||||
try:
|
||||
cpu_freq = psutil.cpu_freq()
|
||||
except NotImplementedError:
|
||||
cpu_freq = psutil._common.scpufreq(current=0, min=0, max=0)
|
||||
node_stats = {
|
||||
'boot_time': str(boot_time),
|
||||
'cpu_usage': psutil.cpu_percent(interval=0.5) / psutil.cpu_count(),
|
||||
'cpu_count': psutil.cpu_count(),
|
||||
'cpu_cur_freq': round(psutil.cpu_freq()[0], 2),
|
||||
'cpu_max_freq': psutil.cpu_freq()[2],
|
||||
'cpu_cur_freq': round(cpu_freq[0], 2),
|
||||
'cpu_max_freq': cpu_freq[2],
|
||||
'mem_percent': psutil.virtual_memory()[2],
|
||||
'mem_usage': helper.human_readable_file_size(psutil.virtual_memory()[3]),
|
||||
'mem_total': helper.human_readable_file_size(psutil.virtual_memory()[0]),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
import logging
|
||||
from sys import modules
|
||||
import sys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -11,18 +11,20 @@ try:
|
||||
except ModuleNotFoundError as e:
|
||||
logging.critical("Import Error: Unable to load {} module".format(e, e.name))
|
||||
print("Import Error: Unable to load {} module".format(e, e.name))
|
||||
pass
|
||||
from app.classes.shared.installer import installer
|
||||
installer.do_install()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Console:
|
||||
|
||||
def __init__(self):
|
||||
if 'colorama' in modules:
|
||||
if 'colorama' in sys.modules:
|
||||
init()
|
||||
|
||||
@staticmethod
|
||||
def do_print(message, color):
|
||||
if 'termcolor' in modules or 'colorama' in modules:
|
||||
if 'termcolor' in sys.modules or 'colorama' in sys.modules:
|
||||
print(colored(message, color))
|
||||
else:
|
||||
print(message)
|
||||
|
@ -51,7 +51,7 @@ class Controller:
|
||||
continue
|
||||
|
||||
# if this server path no longer exists - let's warn and bomb out
|
||||
if not helper.check_path_exits(s['path']):
|
||||
if not helper.check_path_exists(s['path']):
|
||||
logger.warning("Unable to find server {} at path {}. Skipping this server".format(s['server_name'],
|
||||
s['path']))
|
||||
|
||||
@ -212,7 +212,9 @@ class Controller:
|
||||
logger.error("Unable to create required server files due to :{}".format(e))
|
||||
return False
|
||||
|
||||
server_command = 'java -Xms{}G -Xmx{}G -jar {} nogui'.format(min_mem, max_mem, full_jar_path)
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
server_log_file = "{}/logs/latest.log".format(server_dir)
|
||||
server_stop = "stop"
|
||||
|
||||
@ -224,7 +226,7 @@ class Controller:
|
||||
|
||||
@staticmethod
|
||||
def verify_jar_server( server_path: str, server_jar: str):
|
||||
path_check = helper.check_path_exits(server_path)
|
||||
path_check = helper.check_path_exists(server_path)
|
||||
jar_check = helper.check_file_exists(os.path.join(server_path, server_jar))
|
||||
if not path_check or not jar_check:
|
||||
return False
|
||||
@ -245,7 +247,9 @@ class Controller:
|
||||
dir_util.copy_tree(server_path, new_server_dir)
|
||||
|
||||
full_jar_path = os.path.join(new_server_dir, server_jar)
|
||||
server_command = 'java -Xms{}G -Xmx{}G -jar {} nogui'.format(min_mem, max_mem, full_jar_path)
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
server_log_file = "{}/logs/latest.log".format(new_server_dir)
|
||||
server_stop = "stop"
|
||||
|
||||
@ -264,7 +268,10 @@ class Controller:
|
||||
return "false"
|
||||
|
||||
full_jar_path = os.path.join(new_server_dir, server_jar)
|
||||
server_command = 'java -Xms{}G -Xmx{}G -jar {} nogui'.format(min_mem, max_mem, full_jar_path)
|
||||
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
|
||||
helper.float_to_string(max_mem),
|
||||
full_jar_path)
|
||||
print('command: ' + server_command)
|
||||
server_log_file = "{}/logs/latest.log".format(new_server_dir)
|
||||
server_stop = "stop"
|
||||
|
||||
|
@ -9,6 +9,7 @@ import base64
|
||||
import socket
|
||||
import random
|
||||
import logging
|
||||
import html
|
||||
|
||||
from datetime import datetime
|
||||
from socket import gethostname
|
||||
@ -45,6 +46,10 @@ class Helpers:
|
||||
self.passhasher = PasswordHasher()
|
||||
self.exiting = False
|
||||
|
||||
def float_to_string(self, gbs: int):
|
||||
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
|
||||
return s
|
||||
|
||||
def check_file_perms(self, path):
|
||||
try:
|
||||
fp = open(path, "r").close()
|
||||
@ -61,8 +66,27 @@ class Helpers:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
logger.error("{} does not exits".format(file))
|
||||
return False
|
||||
logger.error("{} does not exist".format(file))
|
||||
return True
|
||||
|
||||
def check_for_old_logs(self, db_helper):
|
||||
servers = db_helper.get_all_defined_servers()
|
||||
for server in servers:
|
||||
logs_path = os.path.split(server['log_path'])[0]
|
||||
latest_log_file = os.path.split(server['log_path'])[1]
|
||||
logs_delete_after = int(server['logs_delete_after'])
|
||||
if logs_delete_after == 0:
|
||||
continue
|
||||
|
||||
log_files = list(filter(
|
||||
lambda val: val != latest_log_file,
|
||||
os.listdir(logs_path)
|
||||
))
|
||||
for log_file in log_files:
|
||||
log_file_path = os.path.join(logs_path, log_file)
|
||||
if self.check_file_exists(log_file_path) and \
|
||||
self.is_file_older_than_x_days(log_file_path, logs_delete_after):
|
||||
os.remove(log_file_path)
|
||||
|
||||
def get_setting(self, key, default_return=False):
|
||||
|
||||
@ -280,7 +304,7 @@ class Helpers:
|
||||
return "%.1f%s%s" % (num, 'Y', suffix)
|
||||
|
||||
@staticmethod
|
||||
def check_path_exits(path: str):
|
||||
def check_path_exists(path: str):
|
||||
logger.debug('Looking for path: {}'.format(path))
|
||||
|
||||
if os.path.exists(path):
|
||||
@ -463,6 +487,35 @@ class Helpers:
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def generate_tree(folder, output=""):
|
||||
for raw_filename in os.listdir(folder):
|
||||
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">
|
||||
<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)
|
||||
|
||||
output += helper.generate_tree(rel)
|
||||
output += '</ul>\n</li>'
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def in_path(x, y):
|
||||
return os.path.abspath(y).__contains__(os.path.abspath(x))
|
||||
|
||||
|
||||
helper = Helpers()
|
||||
|
25
app/classes/shared/installer.py
Normal file
25
app/classes/shared/installer.py
Normal file
@ -0,0 +1,25 @@
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
class install:
|
||||
|
||||
@staticmethod
|
||||
def is_venv():
|
||||
return (hasattr(sys, 'real_prefix') or
|
||||
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))
|
||||
|
||||
def do_install(self):
|
||||
|
||||
# are we in a venv?
|
||||
if not self.is_venv():
|
||||
print("Crafty Requires a venv to install")
|
||||
sys.exit(1)
|
||||
|
||||
# do our pip install
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", 'requirements.txt'])
|
||||
print("Crafty has installed it's dependencies, please restart Crafty")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
installer = install()
|
@ -34,29 +34,49 @@ class Users(BaseModel):
|
||||
user_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
last_login = DateTimeField(default=datetime.datetime.now)
|
||||
last_update = DateTimeField(default=datetime.datetime.now)
|
||||
last_ip = CharField(default="")
|
||||
username = CharField(default="")
|
||||
username = CharField(default="", unique=True, index=True)
|
||||
password = CharField(default="")
|
||||
enabled = BooleanField(default=True)
|
||||
api_token = CharField(default="")
|
||||
allowed_servers = CharField(default="[]")
|
||||
superuser = BooleanField(default=False)
|
||||
api_token = CharField(default="", unique=True, index=True) # we may need to revisit this
|
||||
|
||||
class Meta:
|
||||
table_name = "users"
|
||||
|
||||
|
||||
class Roles(BaseModel):
|
||||
role_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
last_update = DateTimeField(default=datetime.datetime.now)
|
||||
role_name = CharField(default="", unique=True, index=True)
|
||||
|
||||
class Meta:
|
||||
table_name = "roles"
|
||||
|
||||
|
||||
class User_Roles(BaseModel):
|
||||
user_id = ForeignKeyField(Users, backref='user_role')
|
||||
role_id = ForeignKeyField(Roles, backref='user_role')
|
||||
|
||||
class Meta:
|
||||
table_name = 'user_roles'
|
||||
primary_key = CompositeKey('user_id', 'role_id')
|
||||
|
||||
|
||||
class Audit_Log(BaseModel):
|
||||
audit_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
user_name = CharField(default="")
|
||||
user_id = IntegerField(default=0)
|
||||
user_id = IntegerField(default=0, index=True)
|
||||
source_ip = CharField(default='127.0.0.1')
|
||||
server_id = IntegerField(default=None)
|
||||
server_id = IntegerField(default=None, index=True) # When auditing global events, use server ID 0
|
||||
log_msg = TextField(default='')
|
||||
|
||||
|
||||
class Host_Stats(BaseModel):
|
||||
time = DateTimeField(default=datetime.datetime.now)
|
||||
time = DateTimeField(default=datetime.datetime.now, index=True)
|
||||
boot_time = CharField(default="")
|
||||
cpu_usage = FloatField(default=0)
|
||||
cpu_cores = IntegerField(default=0)
|
||||
@ -74,8 +94,8 @@ class Host_Stats(BaseModel):
|
||||
class Servers(BaseModel):
|
||||
server_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
server_uuid = CharField(default="")
|
||||
server_name = CharField(default="Server")
|
||||
server_uuid = CharField(default="", index=True)
|
||||
server_name = CharField(default="Server", index=True)
|
||||
path = CharField(default="")
|
||||
executable = CharField(default="")
|
||||
log_path = CharField(default="")
|
||||
@ -86,15 +106,34 @@ class Servers(BaseModel):
|
||||
stop_command = CharField(default="stop")
|
||||
server_ip = CharField(default="127.0.0.1")
|
||||
server_port = IntegerField(default=25565)
|
||||
logs_delete_after = IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
table_name = "servers"
|
||||
|
||||
|
||||
class User_Servers(BaseModel):
|
||||
user_id = ForeignKeyField(Users, backref='user_server')
|
||||
server_id = ForeignKeyField(Servers, backref='user_server')
|
||||
|
||||
class Meta:
|
||||
table_name = 'user_servers'
|
||||
primary_key = CompositeKey('user_id', 'server_id')
|
||||
|
||||
|
||||
class Role_Servers(BaseModel):
|
||||
role_id = ForeignKeyField(Roles, backref='role_server')
|
||||
server_id = ForeignKeyField(Servers, backref='role_server')
|
||||
|
||||
class Meta:
|
||||
table_name = 'role_servers'
|
||||
primary_key = CompositeKey('role_id', 'server_id')
|
||||
|
||||
|
||||
class Server_Stats(BaseModel):
|
||||
stats_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
server_id = ForeignKeyField(Servers, backref='server')
|
||||
server_id = ForeignKeyField(Servers, backref='server', index=True)
|
||||
started = CharField(default="")
|
||||
running = BooleanField(default=False)
|
||||
cpu = FloatField(default=0)
|
||||
@ -118,8 +157,8 @@ class Server_Stats(BaseModel):
|
||||
class Commands(BaseModel):
|
||||
command_id = AutoField()
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
server_id = ForeignKeyField(Servers, backref='server')
|
||||
user = ForeignKeyField(Users, backref='user')
|
||||
server_id = ForeignKeyField(Servers, backref='server', index=True)
|
||||
user = ForeignKeyField(Users, backref='user', index=True)
|
||||
source_ip = CharField(default='127.0.0.1')
|
||||
command = CharField(default='')
|
||||
executed = BooleanField(default=False)
|
||||
@ -130,7 +169,7 @@ class Commands(BaseModel):
|
||||
|
||||
class Webhooks(BaseModel):
|
||||
id = AutoField()
|
||||
name = CharField(max_length=64, unique=True)
|
||||
name = CharField(max_length=64, unique=True, index=True)
|
||||
method = CharField(default="POST")
|
||||
url = CharField(unique=True)
|
||||
event = CharField(default="")
|
||||
@ -144,7 +183,7 @@ class Backups(BaseModel):
|
||||
directories = CharField()
|
||||
storage_location = CharField()
|
||||
max_backups = IntegerField()
|
||||
server_id = IntegerField()
|
||||
server_id = IntegerField(index=True)
|
||||
|
||||
class Meta:
|
||||
table_name = 'backups'
|
||||
@ -158,9 +197,13 @@ class db_builder:
|
||||
database.create_tables([
|
||||
Backups,
|
||||
Users,
|
||||
Roles,
|
||||
User_Roles,
|
||||
Host_Stats,
|
||||
Webhooks,
|
||||
Servers,
|
||||
User_Servers,
|
||||
Role_Servers,
|
||||
Server_Stats,
|
||||
Commands,
|
||||
Audit_Log
|
||||
@ -174,16 +217,18 @@ class db_builder:
|
||||
|
||||
username = default_data.get("username", 'admin')
|
||||
password = default_data.get("password", 'crafty')
|
||||
api_token = helper.random_string_generator(32)
|
||||
|
||||
Users.insert({
|
||||
Users.username: username.lower(),
|
||||
Users.password: helper.encode_pass(password),
|
||||
Users.api_token: api_token,
|
||||
Users.enabled: True
|
||||
}).execute()
|
||||
#api_token = helper.random_string_generator(32)
|
||||
#
|
||||
#Users.insert({
|
||||
# Users.username: username.lower(),
|
||||
# Users.password: helper.encode_pass(password),
|
||||
# Users.api_token: api_token,
|
||||
# Users.enabled: True,
|
||||
# Users.superuser: True
|
||||
#}).execute()
|
||||
db_shortcuts.add_user(username, password=password, superuser=True)
|
||||
|
||||
console.info("API token is {}".format(api_token))
|
||||
#console.info("API token is {}".format(api_token))
|
||||
|
||||
@staticmethod
|
||||
def is_fresh_install():
|
||||
@ -197,7 +242,8 @@ class db_builder:
|
||||
|
||||
class db_shortcuts:
|
||||
|
||||
def return_rows(self, query):
|
||||
@staticmethod
|
||||
def return_rows(query):
|
||||
rows = []
|
||||
|
||||
try:
|
||||
@ -210,7 +256,8 @@ class db_shortcuts:
|
||||
|
||||
return rows
|
||||
|
||||
def get_server_data_by_id(self, server_id):
|
||||
@staticmethod
|
||||
def get_server_data_by_id(server_id):
|
||||
try:
|
||||
query = Servers.get_by_id(server_id)
|
||||
except DoesNotExist:
|
||||
@ -218,25 +265,29 @@ class db_shortcuts:
|
||||
|
||||
return model_to_dict(query)
|
||||
|
||||
def get_all_defined_servers(self):
|
||||
@staticmethod
|
||||
def get_all_defined_servers():
|
||||
query = Servers.select()
|
||||
return self.return_rows(query)
|
||||
return db_helper.return_rows(query)
|
||||
|
||||
def get_all_servers_stats(self):
|
||||
servers = self.get_all_defined_servers()
|
||||
@staticmethod
|
||||
def get_all_servers_stats():
|
||||
servers = db_helper.get_all_defined_servers()
|
||||
server_data = []
|
||||
|
||||
for s in servers:
|
||||
latest = Server_Stats.select().where(Server_Stats.server_id == s.get('server_id')).order_by(Server_Stats.created.desc()).limit(1)
|
||||
server_data.append({'server_data': s, "stats": self.return_rows(latest)})
|
||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)})
|
||||
return server_data
|
||||
|
||||
def get_server_stats_by_id(self, server_id):
|
||||
@staticmethod
|
||||
def get_server_stats_by_id(server_id):
|
||||
stats = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1)
|
||||
return self.return_rows(stats)
|
||||
return db_helper.return_rows(stats)
|
||||
|
||||
def server_id_exists(self, server_id):
|
||||
if not self.get_server_data_by_id(server_id):
|
||||
@staticmethod
|
||||
def server_id_exists(server_id):
|
||||
if not db_helper.get_server_data_by_id(server_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -245,13 +296,197 @@ class db_shortcuts:
|
||||
query = Host_Stats.select().order_by(Host_Stats.id.desc()).get()
|
||||
return model_to_dict(query)
|
||||
|
||||
def get_all_users(self):
|
||||
@staticmethod
|
||||
def new_api_token():
|
||||
while True:
|
||||
token = helper.random_string_generator(32)
|
||||
test = list(Users.select(Users.user_id).where(Users.api_token == token))
|
||||
if len(test) == 0:
|
||||
return token
|
||||
|
||||
@staticmethod
|
||||
def get_all_users():
|
||||
query = Users.select()
|
||||
return query
|
||||
|
||||
def get_unactioned_commands(self):
|
||||
@staticmethod
|
||||
def get_all_roles():
|
||||
query = Roles.select()
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def get_userid_by_name(username):
|
||||
try:
|
||||
return (Users.get(Users.username == username)).user_id
|
||||
except DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user(user_id):
|
||||
user = model_to_dict(Users.get(Users.user_id == user_id))
|
||||
|
||||
if user:
|
||||
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
|
||||
else:
|
||||
logger.debug("user: ({}) {}".format(user_id, {}))
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def update_user(user_id, user_data={}):
|
||||
base_data = db_helper.get_user(user_id)
|
||||
up_data = {}
|
||||
added_roles = set()
|
||||
removed_roles = set()
|
||||
added_servers = set()
|
||||
removed_servers = set()
|
||||
for key in user_data:
|
||||
if key == "user_id":
|
||||
continue
|
||||
elif key == "roles":
|
||||
added_roles = user_data['roles'].difference(base_data['roles'])
|
||||
removed_roles = base_data['roles'].difference(user_data['roles'])
|
||||
elif key == "servers":
|
||||
added_servers = user_data['servers'].difference(base_data['servers'])
|
||||
removed_servers = base_data['servers'].difference(user_data['servers'])
|
||||
elif key == "regen_api":
|
||||
if user_data['regen_api']:
|
||||
up_data['api_token'] = db_shortcuts.new_api_token()
|
||||
elif key == "password":
|
||||
if user_data['password'] is not None and user_data['password'] != "":
|
||||
up_data['password'] = helper.encode_pass(user_data['password'])
|
||||
elif base_data[key] != user_data[key]:
|
||||
up_data[key] = user_data[key]
|
||||
up_data['last_update'] = helper.get_time_as_string()
|
||||
logger.debug("user: {} +role:{} -role:{} +server:{} -server{}".format(user_data, added_roles, removed_roles, added_servers, removed_servers))
|
||||
with database.atomic():
|
||||
for role in added_roles:
|
||||
User_Roles.get_or_create(user_id=user_id, role_id=role)
|
||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
||||
User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute()
|
||||
|
||||
for server in added_servers:
|
||||
User_Servers.get_or_create(user_id=user_id, server_id=server)
|
||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
||||
User_Servers.delete().where(User_Servers.user_id == user_id).where(User_Servers.server_id.in_(removed_servers)).execute()
|
||||
if up_data:
|
||||
Users.update(up_data).where(Users.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def add_user(username, password=None, api_token=None, enabled=True, superuser=False):
|
||||
if password is not None:
|
||||
pw_enc = helper.encode_pass(password)
|
||||
else:
|
||||
pw_enc = None
|
||||
if api_token is None:
|
||||
api_token = db_shortcuts.new_api_token()
|
||||
else:
|
||||
if type(api_token) is not str and len(api_token) != 32:
|
||||
raise ValueError("API token must be a 32 character string")
|
||||
user_id = Users.insert({
|
||||
Users.username: username.lower(),
|
||||
Users.password: pw_enc,
|
||||
Users.api_token: api_token,
|
||||
Users.enabled: enabled,
|
||||
Users.superuser: superuser,
|
||||
Users.created: helper.get_time_as_string()
|
||||
}).execute()
|
||||
return user_id
|
||||
|
||||
@staticmethod
|
||||
def remove_user(user_id):
|
||||
user = Users.get(Users.user_id == user_id)
|
||||
return user.delete_instance()
|
||||
|
||||
@staticmethod
|
||||
def user_id_exists(user_id):
|
||||
if not db_shortcuts.get_user(user_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_roleid_by_name(role_name):
|
||||
try:
|
||||
return (Roles.get(Roles.role_name == role_name)).role_id
|
||||
except DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_role(role_id):
|
||||
role = model_to_dict(Roles.get(Roles.role_id == role_id))
|
||||
|
||||
if role:
|
||||
servers_query = Role_Servers.select().join(Servers, JOIN.INNER).where(Role_Servers.role_id == role_id)
|
||||
# TODO: this query needs to be narrower
|
||||
servers = set()
|
||||
for s in servers_query:
|
||||
servers.add(s.server_id.server_id)
|
||||
role['servers'] = servers
|
||||
logger.debug("role: ({}) {}".format(role_id, role))
|
||||
return role
|
||||
else:
|
||||
logger.debug("role: ({}) {}".format(role_id, {}))
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def update_role(role_id, role_data={}):
|
||||
base_data = db_helper.get_role(role_id)
|
||||
up_data = {}
|
||||
added_servers = set()
|
||||
removed_servers = set()
|
||||
for key in role_data:
|
||||
if key == "role_id":
|
||||
continue
|
||||
elif key == "servers":
|
||||
added_servers = role_data['servers'].difference(base_data['servers'])
|
||||
removed_servers = base_data['servers'].difference(role_data['servers'])
|
||||
elif base_data[key] != role_data[key]:
|
||||
up_data[key] = role_data[key]
|
||||
up_data['last_update'] = helper.get_time_as_string()
|
||||
logger.debug("role: {} +server:{} -server{}".format(role_data, added_servers, removed_servers))
|
||||
with database.atomic():
|
||||
for server in added_servers:
|
||||
Role_Servers.get_or_create(role_id=role_id, server_id=server)
|
||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
||||
Role_Servers.delete().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id.in_(removed_servers)).execute()
|
||||
if up_data:
|
||||
Roles.update(up_data).where(Roles.role_id == role_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def add_role(role_name):
|
||||
role_id = Roles.insert({
|
||||
Roles.role_name: role_name.lower(),
|
||||
Roles.created: helper.get_time_as_string()
|
||||
}).execute()
|
||||
return role_id
|
||||
|
||||
@staticmethod
|
||||
def remove_role(role_id):
|
||||
role = Roles.get(Roles.role_id == role_id)
|
||||
return role.delete_instance()
|
||||
|
||||
@staticmethod
|
||||
def role_id_exists(role_id):
|
||||
if not db_shortcuts.get_role(role_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_unactioned_commands():
|
||||
query = Commands.select().where(Commands.executed == 0)
|
||||
return self.return_rows(query)
|
||||
return db_helper.return_rows(query)
|
||||
|
||||
@staticmethod
|
||||
def get_server_friendly_name(server_id):
|
||||
@ -275,11 +510,13 @@ class db_shortcuts:
|
||||
Commands.command: command
|
||||
}).execute()
|
||||
|
||||
def get_actity_log(self):
|
||||
@staticmethod
|
||||
def get_actity_log():
|
||||
q = Audit_Log.select()
|
||||
return self.return_db_rows(q)
|
||||
return db_helper.return_db_rows(q)
|
||||
|
||||
def return_db_rows(self, model):
|
||||
@staticmethod
|
||||
def return_db_rows(model):
|
||||
data = [model_to_dict(row) for row in model]
|
||||
return data
|
||||
|
||||
|
@ -97,7 +97,7 @@ class Server:
|
||||
console.critical("Server executable path: {} does not seem to exist".format(full_path))
|
||||
helper.do_exit()
|
||||
|
||||
if not helper.check_path_exits(self.server_path):
|
||||
if not helper.check_path_exists(self.server_path):
|
||||
logger.critical("Server path: {} does not seem to exits".format(self.server_path))
|
||||
console.critical("Server path: {} does not seem to exits".format(self.server_path))
|
||||
helper.do_exit()
|
||||
|
@ -38,6 +38,9 @@ class TasksManager:
|
||||
|
||||
self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler")
|
||||
|
||||
self.log_watcher_thread = threading.Thread(target=self.log_watcher, daemon=True, name="log_watcher")
|
||||
self.log_watcher_thread.start()
|
||||
|
||||
self.command_thread = threading.Thread(target=self.command_watcher, daemon=True, name="command_watcher")
|
||||
self.command_thread.start()
|
||||
|
||||
|
@ -3,6 +3,8 @@ import logging
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
import bleach
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.shared.models import Users, installer
|
||||
@ -55,7 +57,7 @@ class AjaxHandler(BaseHandler):
|
||||
self.redirect("/panel/error?error=Server ID Not Found")
|
||||
|
||||
if server_data['log_path']:
|
||||
logger.warning("Server ID not found in server_log ajax call")
|
||||
logger.warning("Server ID not found in server_log ajax call ({})".format(server_id))
|
||||
|
||||
if full_log:
|
||||
log_lines = helper.get_setting('max_log_lines')
|
||||
@ -79,7 +81,57 @@ class AjaxHandler(BaseHandler):
|
||||
page_data['notify_data'] = data
|
||||
self.render_page('ajax/notify.html', page_data)
|
||||
|
||||
elif page == "get_file":
|
||||
file_path = self.get_argument('file_path', None)
|
||||
server_id = self.get_argument('id', None)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in get_file ajax call")
|
||||
console.warning("Server ID not found in get_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in get_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in get_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path)\
|
||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning("Invalid path in get_file ajax call ({})".format(file_path))
|
||||
console.warning("Invalid path in get_file ajax call ({})".format(file_path))
|
||||
return False
|
||||
|
||||
file = open(file_path)
|
||||
file_contents = file.read()
|
||||
file.close()
|
||||
|
||||
self.write(file_contents)
|
||||
self.finish()
|
||||
|
||||
elif page == "get_tree":
|
||||
server_id = self.get_argument('id', None)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in get_file ajax call")
|
||||
console.warning("Server ID not found in get_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in get_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in get_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
self.write(db_helper.get_server_data_by_id(server_id)['path'] + '\n' +
|
||||
helper.generate_tree(db_helper.get_server_data_by_id(server_id)['path']))
|
||||
self.finish()
|
||||
|
||||
@tornado.web.authenticated
|
||||
def post(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||
@ -90,11 +142,12 @@ class AjaxHandler(BaseHandler):
|
||||
}
|
||||
|
||||
if page == "send_command":
|
||||
command = bleach.clean(self.get_body_argument('command', default=None, strip=True))
|
||||
server_id = bleach.clean(self.get_argument('id'))
|
||||
command = self.get_body_argument('command', default=None, strip=True)
|
||||
server_id = self.get_argument('id')
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in send_command ajax call")
|
||||
console.warning("Server ID not found in send_command ajax call")
|
||||
|
||||
srv_obj = controller.get_server_obj(server_id)
|
||||
|
||||
@ -102,3 +155,192 @@ class AjaxHandler(BaseHandler):
|
||||
if srv_obj.check_running():
|
||||
srv_obj.send_command(command)
|
||||
|
||||
elif page == "create_file":
|
||||
file_parent = self.get_body_argument('file_parent', default=None, strip=True)
|
||||
file_name = self.get_body_argument('file_name', default=None, strip=True)
|
||||
file_path = os.path.join(file_parent, file_name)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in create_file ajax call")
|
||||
console.warning("Server ID not found in create_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in create_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in create_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path) \
|
||||
or helper.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning("Invalid path in create_file ajax call ({})".format(file_path))
|
||||
console.warning("Invalid path in create_file ajax call ({})".format(file_path))
|
||||
return False
|
||||
|
||||
# Create the file by opening it
|
||||
with open(file_path, 'w') as file_object:
|
||||
file_object.close()
|
||||
|
||||
elif page == "create_dir":
|
||||
dir_parent = self.get_body_argument('dir_parent', default=None, strip=True)
|
||||
dir_name = self.get_body_argument('dir_name', default=None, strip=True)
|
||||
dir_path = os.path.join(dir_parent, dir_name)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in create_dir ajax call")
|
||||
console.warning("Server ID not found in create_dir ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in create_dir ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in create_dir ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], dir_path) \
|
||||
or helper.check_path_exists(os.path.abspath(dir_path)):
|
||||
logger.warning("Invalid path in create_dir ajax call ({})".format(dir_path))
|
||||
console.warning("Invalid path in create_dir ajax call ({})".format(dir_path))
|
||||
return False
|
||||
|
||||
# Create the directory
|
||||
os.mkdir(dir_path)
|
||||
|
||||
@tornado.web.authenticated
|
||||
def delete(self, page):
|
||||
if page == "del_file":
|
||||
file_path = self.get_body_argument('file_path', default=None, strip=True)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in del_file ajax call")
|
||||
console.warning("Server ID not found in del_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in del_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path) \
|
||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning("Invalid path in del_file ajax call ({})".format(file_path))
|
||||
console.warning("Invalid path in del_file ajax call ({})".format(file_path))
|
||||
return False
|
||||
|
||||
# Delete the file
|
||||
os.remove(file_path)
|
||||
|
||||
elif page == "del_dir":
|
||||
dir_path = self.get_body_argument('dir_path', default=None, strip=True)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in del_file ajax call")
|
||||
console.warning("Server ID not found in del_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in del_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], dir_path) \
|
||||
or not helper.check_path_exists(os.path.abspath(dir_path)):
|
||||
logger.warning("Invalid path in del_file ajax call ({})".format(dir_path))
|
||||
console.warning("Invalid path in del_file ajax call ({})".format(dir_path))
|
||||
return False
|
||||
|
||||
# Delete the file
|
||||
# os.rmdir(dir_path)
|
||||
shutil.rmtree(dir_path) # Removes also when there are contents
|
||||
|
||||
@tornado.web.authenticated
|
||||
def put(self, page):
|
||||
if page == "save_file":
|
||||
file_contents = self.get_body_argument('file_contents', default=None, strip=True)
|
||||
file_path = self.get_body_argument('file_path', default=None, strip=True)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(file_contents)
|
||||
print(file_path)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in save_file ajax call")
|
||||
console.warning("Server ID not found in save_file ajax call")
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in save_file ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in save_file ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path)\
|
||||
or not helper.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning("Invalid path in save_file ajax call ({})".format(file_path))
|
||||
console.warning("Invalid path in save_file ajax call ({})".format(file_path))
|
||||
return False
|
||||
|
||||
# Open the file in write mode and store the content in file_object
|
||||
with open(file_path, 'w') as file_object:
|
||||
file_object.write(file_contents)
|
||||
|
||||
elif page == "rename_item":
|
||||
item_path = self.get_body_argument('item_path', default=None, strip=True)
|
||||
new_item_name = self.get_body_argument('new_item_name', default=None, strip=True)
|
||||
server_id = self.get_argument('id', None)
|
||||
print(server_id)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
|
||||
return False
|
||||
else:
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
# does this server id exist?
|
||||
if not db_helper.server_id_exists(server_id):
|
||||
logger.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
|
||||
console.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
if item_path is None or new_item_name is None:
|
||||
logger.warning("Invalid path in rename_item ajax call")
|
||||
console.warning("Invalid path in rename_item ajax call")
|
||||
return False
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], item_path) \
|
||||
or not helper.check_path_exists(os.path.abspath(item_path)):
|
||||
logger.warning("Invalid path in rename_item ajax call ({})".format(server_id))
|
||||
console.warning("Invalid path in rename_item ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
||||
|
||||
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], new_item_path) \
|
||||
or helper.check_path_exists(os.path.abspath(new_item_path)):
|
||||
logger.warning("Invalid path 2 in rename_item ajax call ({})".format(server_id))
|
||||
console.warning("Invalid path 2 in rename_item ajax call ({})".format(server_id))
|
||||
return False
|
||||
|
||||
# RENAME
|
||||
os.rename(item_path, new_item_path)
|
||||
|
@ -3,6 +3,8 @@ import logging
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
import bleach
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.shared.models import Users, installer
|
||||
@ -23,6 +25,9 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
template = "panel/denied.html"
|
||||
|
||||
now = time.time()
|
||||
formatted_time = str(datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S'))
|
||||
|
||||
defined_servers = controller.list_defined_servers()
|
||||
|
||||
page_data = {
|
||||
@ -38,7 +43,8 @@ class PanelHandler(BaseHandler):
|
||||
'menu_servers': defined_servers,
|
||||
'hosts_data': db_helper.get_latest_hosts_stats(),
|
||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
||||
'error': error
|
||||
'error': error,
|
||||
'time': formatted_time
|
||||
}
|
||||
|
||||
# if no servers defined, let's go to the build server area
|
||||
@ -59,12 +65,6 @@ class PanelHandler(BaseHandler):
|
||||
elif page == 'contribute':
|
||||
template = "panel/contribute.html"
|
||||
|
||||
elif page == 'file_edit':
|
||||
template = "panel/file_edit.html"
|
||||
|
||||
elif page == 'files_menu':
|
||||
template = "panel/files_menu.html"
|
||||
|
||||
elif page == "remove_server":
|
||||
server_id = self.get_argument('id', None)
|
||||
server_data = controller.get_server_data(server_id)
|
||||
@ -106,10 +106,12 @@ class PanelHandler(BaseHandler):
|
||||
self.redirect("/panel/error?error=Invalid Server ID")
|
||||
return False
|
||||
|
||||
valid_subpages = ['term', 'logs', 'config']
|
||||
valid_subpages = ['term', 'logs', 'config', 'files']
|
||||
|
||||
if subpage not in valid_subpages:
|
||||
logger.debug('not a valid subpage')
|
||||
subpage = 'term'
|
||||
logger.debug('Subpage: "{}"'.format(subpage))
|
||||
|
||||
# server_data isn't needed since the server_stats also pulls server data
|
||||
# page_data['server_data'] = db_helper.get_server_data_by_id(server_id)
|
||||
@ -120,9 +122,150 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
elif page == 'panel_config':
|
||||
page_data['users'] = db_helper.get_all_users()
|
||||
# print(page_data['users'])
|
||||
page_data['roles'] = db_helper.get_all_roles()
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
for user in page_data['users']:
|
||||
if user.user_id != exec_user['user_id']:
|
||||
user.api_token = "********"
|
||||
template = "panel/panel_config.html"
|
||||
|
||||
elif page == "add_user":
|
||||
page_data['new_user'] = True
|
||||
page_data['user'] = {}
|
||||
page_data['user']['username'] = ""
|
||||
page_data['user']['user_id'] = -1
|
||||
page_data['user']['enabled'] = True
|
||||
page_data['user']['superuser'] = False
|
||||
page_data['user']['api_token'] = "N/A"
|
||||
page_data['user']['created'] = "N/A"
|
||||
page_data['user']['last_login'] = "N/A"
|
||||
page_data['user']['last_ip'] = "N/A"
|
||||
page_data['role']['last_update'] = "N/A"
|
||||
page_data['user']['roles'] = set()
|
||||
page_data['user']['servers'] = set()
|
||||
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
|
||||
page_data['roles_all'] = db_helper.get_all_roles()
|
||||
page_data['servers_all'] = controller.list_defined_servers()
|
||||
template = "panel/panel_edit_user.html"
|
||||
|
||||
elif page == "edit_user":
|
||||
page_data['new_user'] = False
|
||||
user_id = self.get_argument('id', None)
|
||||
page_data['user'] = db_helper.get_user(user_id)
|
||||
page_data['roles_all'] = db_helper.get_all_roles()
|
||||
page_data['servers_all'] = controller.list_defined_servers()
|
||||
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif user_id is None:
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
return False
|
||||
|
||||
if exec_user['user_id'] != page_data['user']['user_id']:
|
||||
page_data['user']['api_token'] = "********"
|
||||
template = "panel/panel_edit_user.html"
|
||||
|
||||
elif page == "remove_user":
|
||||
user_id = bleach.clean(self.get_argument('id', None))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif user_id is None:
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
target_user = db_helper.get_user(user_id)
|
||||
if not target_user:
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
return False
|
||||
elif target_user['superuser']:
|
||||
self.redirect("/panel/error?error=Cannot remove a superuser")
|
||||
return False
|
||||
|
||||
db_helper.remove_user(user_id)
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Removed user {} (UID:{})".format(target_user['username'], user_id),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
||||
|
||||
elif page == "add_role":
|
||||
page_data['new_role'] = True
|
||||
page_data['role'] = {}
|
||||
page_data['role']['role_name'] = ""
|
||||
page_data['role']['role_id'] = -1
|
||||
page_data['role']['created'] = "N/A"
|
||||
page_data['role']['last_update'] = "N/A"
|
||||
page_data['role']['servers'] = set()
|
||||
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
|
||||
page_data['servers_all'] = controller.list_defined_servers()
|
||||
template = "panel/panel_edit_role.html"
|
||||
|
||||
elif page == "edit_role":
|
||||
page_data['new_role'] = False
|
||||
role_id = self.get_argument('id', None)
|
||||
page_data['role'] = db_helper.get_role(role_id)
|
||||
page_data['servers_all'] = controller.list_defined_servers()
|
||||
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif role_id is None:
|
||||
self.redirect("/panel/error?error=Invalid Role ID")
|
||||
return False
|
||||
|
||||
template = "panel/panel_edit_role.html"
|
||||
|
||||
elif page == "remove_role":
|
||||
role_id = bleach.clean(self.get_argument('id', None))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif role_id is None:
|
||||
self.redirect("/panel/error?error=Invalid Role ID")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
target_role = db_helper.get_user(role_id)
|
||||
if not target_role:
|
||||
self.redirect("/panel/error?error=Invalid Role ID")
|
||||
return False
|
||||
|
||||
db_helper.remove_role(role_id)
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Removed role {} (RID:{})".format(target_role['role_name'], role_id),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
||||
|
||||
elif page == "activity_logs":
|
||||
page_data['audit_logs'] = db_helper.get_actity_log()
|
||||
|
||||
@ -130,7 +273,9 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
self.render(
|
||||
template,
|
||||
data=page_data
|
||||
data=page_data,
|
||||
time=time,
|
||||
utc_offset=(time.timezone * -1 / 60 / 60),
|
||||
)
|
||||
|
||||
@tornado.web.authenticated
|
||||
@ -149,9 +294,16 @@ class PanelHandler(BaseHandler):
|
||||
server_port = self.get_argument('server_port', None)
|
||||
auto_start = int(float(self.get_argument('auto_start', '0')))
|
||||
crash_detection = int(float(self.get_argument('crash_detection', '0')))
|
||||
logs_delete_after = int(float(self.get_argument('logs_delete_after', '0')))
|
||||
subpage = self.get_argument('subpage', None)
|
||||
|
||||
if server_id is None:
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user.superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif server_id is None:
|
||||
self.redirect("/panel/error?error=Invalid Server ID")
|
||||
return False
|
||||
else:
|
||||
@ -174,15 +326,223 @@ class PanelHandler(BaseHandler):
|
||||
Servers.server_port: server_port,
|
||||
Servers.auto_start: auto_start,
|
||||
Servers.crash_detection: crash_detection,
|
||||
Servers.logs_delete_after: logs_delete_after,
|
||||
}).where(Servers.server_id == server_id).execute()
|
||||
|
||||
controller.refresh_server_settings(server_id)
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
|
||||
db_helper.add_to_audit_log(user_data['user_id'],
|
||||
"Edited server {} named {}".format(server_id, server_name),
|
||||
server_id,
|
||||
self.get_remote_ip())
|
||||
|
||||
self.redirect("/panel/server_detail?id={}&subpage=config".format(server_id))
|
||||
|
||||
elif page == "edit_user":
|
||||
user_id = bleach.clean(self.get_argument('id', None))
|
||||
username = bleach.clean(self.get_argument('username', None))
|
||||
password0 = bleach.clean(self.get_argument('password0', None))
|
||||
password1 = bleach.clean(self.get_argument('password1', None))
|
||||
enabled = int(float(bleach.clean(self.get_argument('enabled'), '0')))
|
||||
regen_api = int(float(bleach.clean(self.get_argument('regen_api', '0'))))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif username is None or username == "":
|
||||
self.redirect("/panel/error?error=Invalid username")
|
||||
return False
|
||||
elif user_id is None:
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
if not db_helper.user_id_exists(user_id):
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
return False
|
||||
|
||||
if password0 != password1:
|
||||
self.redirect("/panel/error?error=Passwords must match")
|
||||
return False
|
||||
|
||||
roles = set()
|
||||
for role in db_helper.get_all_roles():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('role_{}_membership'.format(role.role_id), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
roles.add(role.role_id)
|
||||
|
||||
servers = set()
|
||||
for server in controller.list_defined_servers():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('server_{}_access'.format(server['server_id']), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
servers.add(server['server_id'])
|
||||
|
||||
user_data = {
|
||||
"username": username,
|
||||
"password": password0,
|
||||
"enabled": enabled,
|
||||
"regen_api": regen_api,
|
||||
"roles": roles,
|
||||
"servers": servers
|
||||
}
|
||||
db_helper.update_user(user_id, user_data=user_data)
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Edited user {} (UID:{}) with roles {} and servers {}".format(username, user_id, roles, servers),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
||||
|
||||
|
||||
elif page == "add_user":
|
||||
username = bleach.clean(self.get_argument('username', None))
|
||||
password0 = bleach.clean(self.get_argument('password0', None))
|
||||
password1 = bleach.clean(self.get_argument('password1', None))
|
||||
enabled = int(float(bleach.clean(self.get_argument('enabled'), '0')))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif username is None or username == "":
|
||||
self.redirect("/panel/error?error=Invalid username")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
if db_helper.get_userid_by_name(username) is not None:
|
||||
self.redirect("/panel/error?error=User exists")
|
||||
return False
|
||||
|
||||
if password0 != password1:
|
||||
self.redirect("/panel/error?error=Passwords must match")
|
||||
return False
|
||||
|
||||
roles = set()
|
||||
for role in db_helper.get_all_roles():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('role_{}_membership'.format(role.role_id), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
roles.add(role['role_id'])
|
||||
|
||||
servers = set()
|
||||
for server in controller.list_defined_servers():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('server_{}_access'.format(server['server_id']), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
servers.add(server['server_id'])
|
||||
|
||||
user_id = db_helper.add_user(username, password=password0, enabled=enabled)
|
||||
db_helper.update_user(user_id, {"roles":roles, "servers": servers})
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Added user {} (UID:{})".format(username, user_id),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Edited user {} (UID:{}) with roles {} and servers {}".format(username, user_id, roles, servers),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
||||
|
||||
elif page == "edit_role":
|
||||
role_id = bleach.clean(self.get_argument('id', None))
|
||||
role_name = bleach.clean(self.get_argument('role_name', None))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif role_name is None or role_name == "":
|
||||
self.redirect("/panel/error?error=Invalid username")
|
||||
return False
|
||||
elif role_id is None:
|
||||
self.redirect("/panel/error?error=Invalid Role ID")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
if not db_helper.role_id_exists(role_id):
|
||||
self.redirect("/panel/error?error=Invalid Role ID")
|
||||
return False
|
||||
|
||||
servers = set()
|
||||
for server in controller.list_defined_servers():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('server_{}_access'.format(server['server_id']), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
servers.add(server['server_id'])
|
||||
|
||||
role_data = {
|
||||
"role_name": role_name,
|
||||
"servers": servers
|
||||
}
|
||||
db_helper.update_role(role_id, role_data=role_data)
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Edited role {} (RID:{}) with servers {}".format(role_name, role_id, servers),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
||||
|
||||
|
||||
elif page == "add_role":
|
||||
role_name = bleach.clean(self.get_argument('role_name', None))
|
||||
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user = db_helper.get_user(user_data['user_id'])
|
||||
if not exec_user['superuser']:
|
||||
self.redirect("/panel/error?error=Unauthorized access: not superuser")
|
||||
return False
|
||||
elif role_name is None or role_name == "":
|
||||
self.redirect("/panel/error?error=Invalid role name")
|
||||
return False
|
||||
else:
|
||||
# does this user id exist?
|
||||
if db_helper.get_roleid_by_name(role_name) is not None:
|
||||
self.redirect("/panel/error?error=Role exists")
|
||||
return False
|
||||
|
||||
servers = set()
|
||||
for server in controller.list_defined_servers():
|
||||
argument = int(float(
|
||||
bleach.clean(
|
||||
self.get_argument('server_{}_access'.format(server['server_id']), '0')
|
||||
)
|
||||
))
|
||||
if argument:
|
||||
servers.add(server['server_id'])
|
||||
|
||||
role_id = db_helper.add_role(role_name)
|
||||
db_helper.update_role(role_id, {"servers": servers})
|
||||
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Added role {} (RID:{})".format(role_name, role_id),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
db_helper.add_to_audit_log(exec_user['user_id'],
|
||||
"Edited role {} (RID:{}) with servers {}".format(role_name, role_id, servers),
|
||||
server_id=0,
|
||||
source_ip=self.get_remote_ip())
|
||||
self.redirect("/panel/panel_config")
|
@ -106,7 +106,7 @@ class PublicHandler(BaseHandler):
|
||||
cookie_data = {
|
||||
"username": user_data.username,
|
||||
"user_id": user_data.user_id,
|
||||
"account_type": user_data.allowed_servers,
|
||||
"account_type": user_data.superuser,
|
||||
}
|
||||
|
||||
self.set_secure_cookie('user_data', json.dumps(cookie_data))
|
||||
|
@ -1,6 +1,8 @@
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.web.base_handler import BaseHandler
|
||||
@ -75,6 +77,64 @@ class ServerHandler(BaseHandler):
|
||||
command = bleach.clean(self.get_argument("command", None))
|
||||
|
||||
if server_id is not None:
|
||||
if command == "clone_server":
|
||||
def is_name_used(name):
|
||||
for server in db_helper.get_all_defined_servers():
|
||||
if server['server_name'] == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
server_data = db_helper.get_server_data_by_id(server_id)
|
||||
server_uuid = server_data.get('server_uuid')
|
||||
new_server_name = server_data.get('server_name') + " (Copy)"
|
||||
|
||||
name_counter = 1
|
||||
while is_name_used(new_server_name):
|
||||
name_counter += 1
|
||||
new_server_name = server_data.get('server_name') + " (Copy {})".format(name_counter)
|
||||
|
||||
console.debug('new_server_name: "{}"'.format(new_server_name))
|
||||
|
||||
new_server_uuid = helper.create_uuid()
|
||||
while os.path.exists(os.path.join(helper.servers_dir, new_server_uuid)):
|
||||
new_server_uuid = helper.create_uuid()
|
||||
new_server_path = os.path.join(helper.servers_dir, new_server_uuid)
|
||||
|
||||
# copy the old server
|
||||
shutil.copytree(server_data.get('path'), new_server_path)
|
||||
|
||||
# TODO get old server DB data to individual variables
|
||||
stop_command = server_data.get('stop_command')
|
||||
new_server_command = str(server_data.get('execution_command')).replace(server_uuid, new_server_uuid)
|
||||
new_executable = server_data.get('executable')
|
||||
new_server_log_file = str(server_data.get('log_path')).replace(server_uuid, new_server_uuid)
|
||||
auto_start = server_data.get('auto_start')
|
||||
auto_start_delay = server_data.get('auto_start_delay')
|
||||
crash_detection = server_data.get('crash_detection')
|
||||
server_port = server_data.get('server_port')
|
||||
|
||||
|
||||
# TODO create the server on the DB side
|
||||
|
||||
Servers.insert({
|
||||
Servers.server_name: new_server_name,
|
||||
Servers.server_uuid: new_server_uuid,
|
||||
Servers.path: new_server_path,
|
||||
Servers.executable: new_executable,
|
||||
Servers.execution_command: new_server_command,
|
||||
Servers.auto_start: auto_start,
|
||||
Servers.auto_start_delay: auto_start_delay,
|
||||
Servers.crash_detection: crash_detection,
|
||||
Servers.log_path: new_server_log_file,
|
||||
Servers.server_port: server_port,
|
||||
Servers.stop_command: stop_command
|
||||
}).execute()
|
||||
|
||||
controller.init_all_servers()
|
||||
console.debug('initted all servers')
|
||||
|
||||
return
|
||||
|
||||
db_helper.send_command(user_data['user_id'], server_id, self.get_remote_ip(), command)
|
||||
|
||||
if page == "step1":
|
||||
|
BIN
app/frontend/static/assets/images/credits/lukas_cropped.png
Normal file
BIN
app/frontend/static/assets/images/credits/lukas_cropped.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
@ -2,7 +2,7 @@
|
||||
<footer class="footer">
|
||||
<div class="container-fluid ">
|
||||
|
||||
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">Copyright © 2020 <a href="http://www.craftycontrol.com/" target="_blank">Crafty Controller</a>. All rights reserved.</span>
|
||||
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">Copyright © 2021 <a href="http://www.craftycontrol.com/" target="_blank">Crafty Controller</a>. All rights reserved.</span>
|
||||
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Version: {{ data['version_data'] }}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -64,6 +64,14 @@
|
||||
</li>
|
||||
{% end %}
|
||||
|
||||
<!--
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/files?id=1">
|
||||
<i class="fas fa-copy"></i>
|
||||
<span class="menu-title">Files Test</span>
|
||||
</a>
|
||||
</li>-->
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
@ -375,6 +375,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-5">
|
||||
<div class="card rounded shadow-none">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="user-avatar mb-auto">
|
||||
<img src="/static/assets/images/credits/lukas_cropped.png"
|
||||
alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="wrapper d-flex align-items-center">
|
||||
<h4 class="mb-0 font-weight-medium">ThatOneLukas</h4>
|
||||
</div>
|
||||
|
||||
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
|
||||
<i class="mdi mdi-map-marker-outline mr-2"></i>
|
||||
<p class="mb-0 text-muted">Helsinki, FI</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="wrapper d-flex align-items-start">
|
||||
<span class="btn btn-sm btn-info mr-2">Staff</span>
|
||||
<span class="btn btn-sm btn-primary mr-2">Developer</span>
|
||||
</div>
|
||||
|
||||
<div class="wrapper d-flex align-items-start pt-3">
|
||||
His interests include programming, gaming, and electronics.
|
||||
He likes gaming, programming, messing around with electronics, and time with his family.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- end user row-->
|
||||
|
||||
</div>
|
||||
@ -471,6 +512,7 @@
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
|
@ -130,6 +130,7 @@
|
||||
<a class="restart_button" data-id="{{server['server_data']['server_id']}}"> <i class="fas fa-sync"></i></a>
|
||||
{% else %}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button"><i class="fas fa-play"></i></a>
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button"> <i class="fas fa-clone"></i></a>
|
||||
{% end %}
|
||||
|
||||
</td>
|
||||
@ -217,7 +218,7 @@
|
||||
<script>
|
||||
|
||||
function send_command (server_id, command){
|
||||
<!-- this getCookie function is in base.html-->
|
||||
/* this getCookie function is in base.html */
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
@ -281,6 +282,16 @@ $( document ).ready(function() {
|
||||
});
|
||||
}
|
||||
|
||||
$( ".clone_button" ).click(function() {
|
||||
server_id = $(this).attr("data-id");
|
||||
send_command(server_id, 'clone_server');
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: "Sending your command",
|
||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> Please be patient while we clone the server<br /> This screen will refresh in a moment </div>'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,190 +0,0 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Editing File -< name >- in server -< server name >- (-< server path id >-){% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
Editing File -< name >- in server -< server name >- (-< server path id >-)
|
||||
<br />
|
||||
<small>Path: -< file location relative to server directory >-</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<a href="/" class="btn btn-link btn-outline-primary" style="position: relative;top: -1rem">
|
||||
<i class="fas fa-arrow-left"></i> Back
|
||||
</a>
|
||||
|
||||
<h3 id="file_warn"></h3>
|
||||
|
||||
<div id="editor">-< file_contents >-</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script src="/static/assets/vendors/ace-builds/src-min/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script>
|
||||
|
||||
let editor = ace.edit('editor');
|
||||
editor.setTheme('ace/theme/dracula');
|
||||
|
||||
let extensionChanges = [
|
||||
{
|
||||
regex: /^js$/,
|
||||
replaceWith: 'ace/mode/javascript'
|
||||
},
|
||||
{
|
||||
regex: /^py$/,
|
||||
replaceWith: 'ace/mode/python'
|
||||
},
|
||||
{
|
||||
regex: /^html$/,
|
||||
replaceWith: 'ace/mode/html'
|
||||
},
|
||||
{
|
||||
regex: /^yml$/,
|
||||
replaceWith: 'ace/mode/yaml'
|
||||
},
|
||||
{
|
||||
regex: /^yaml$/,
|
||||
replaceWith: 'ace/mode/yaml'
|
||||
},
|
||||
{
|
||||
regex: /^txt$/,
|
||||
replaceWith: 'ace/mode/text'
|
||||
},
|
||||
{
|
||||
regex: /^json$/,
|
||||
replaceWith: 'ace/mode/json'
|
||||
},
|
||||
{
|
||||
regex: /^java$/,
|
||||
replaceWith: 'ace/mode/java'
|
||||
},
|
||||
{
|
||||
regex: /^cpp$/,
|
||||
replaceWith: 'ace/mode/c_cpp'
|
||||
},
|
||||
{
|
||||
regex: /^css$/,
|
||||
replaceWith: 'ace/mode/css'
|
||||
},
|
||||
{
|
||||
regex: /^scss$/,
|
||||
replaceWith: 'ace/mode/scss'
|
||||
},
|
||||
{
|
||||
regex: /^sass$/,
|
||||
replaceWith: 'ace/mode/sass'
|
||||
},
|
||||
{
|
||||
regex: /^lua$/,
|
||||
replaceWith: 'ace/mode/lua'
|
||||
},
|
||||
{
|
||||
regex: /^php$/,
|
||||
replaceWith: 'ace/mode/php'
|
||||
},
|
||||
{
|
||||
regex: /^ps1$/,
|
||||
replaceWith: 'ace/mode/powershell'
|
||||
},
|
||||
{
|
||||
regex: /^svg$/,
|
||||
replaceWith: 'ace/mode/svg'
|
||||
},
|
||||
{
|
||||
regex: /^sh$/,
|
||||
replaceWith: 'ace/mode/sh'
|
||||
},
|
||||
{
|
||||
regex: /^xml$/,
|
||||
replaceWith: 'ace/mode/xml'
|
||||
},
|
||||
{
|
||||
regex: /^ts$/,
|
||||
replaceWith: 'ace/mode/typescript'
|
||||
}
|
||||
];
|
||||
|
||||
let fileName = '-< file_name >-.txt';
|
||||
|
||||
if (fileName.match('.')) {
|
||||
|
||||
// The pop method removes and returns the last element.
|
||||
setMode(fileName
|
||||
.split('.')
|
||||
.pop()
|
||||
.replace('ace/mode/', ''));
|
||||
|
||||
} else {
|
||||
setMode('txt');
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = 'Warning: This is not a supported language';
|
||||
}
|
||||
|
||||
function setMode (extension) {
|
||||
// if the extension matches with the RegEx it will return the replaceWith
|
||||
// property. else it will return the one it has. defaults to the extension.
|
||||
// this runs for each element in extensionChanges.
|
||||
let aceMode = extensionChanges.reduce((output, element) => {
|
||||
return extension.match(element.regex)
|
||||
? element.replaceWith
|
||||
: output;
|
||||
}, extension);
|
||||
|
||||
if (!aceMode.startsWith('ace/mode/')) {
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = 'Warning: This is not a supported language';
|
||||
} else {
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = '';
|
||||
|
||||
console.log(aceMode || 'ace/mode/text');
|
||||
editor.session.setMode(aceMode || 'ace/mode/text');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function save() {
|
||||
let text = editor.session.getValue();
|
||||
// Use AJAX or something
|
||||
alert(text);
|
||||
}
|
||||
|
||||
</script>
|
||||
{% end %}
|
@ -1,112 +0,0 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Looking at files in server -< server name >- (-< server path id >-){% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
Looking at files in server -< server name >- (-< server path id >-)
|
||||
<br />
|
||||
<small>Path: -< location relative to server directory >-</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<ul class="files-list list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
||||
style="background-color: var(--dark) !important;">
|
||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="text-white">-< name >-</a>
|
||||
<span>
|
||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="mx-1 btn btn-dark">Edit</a>
|
||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
||||
<span class="badge badge-primary badge-pill">File</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
||||
style="background-color: var(--dark) !important;">
|
||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="text-white">-< name >-</a>
|
||||
<span>
|
||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="mx-1 btn btn-dark">Open</a>
|
||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
||||
<span class="badge badge-primary badge-pill">Folder</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<!--
|
||||
|
||||
File:
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
||||
style="background-color: var(--dark) !important;">
|
||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="text-white">-< name >-</a>
|
||||
<span>
|
||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="mx-1 btn btn-dark">Edit</a>
|
||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
||||
<span class="badge badge-primary badge-pill">File</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
Folder:
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
||||
style="background-color: var(--dark) !important;">
|
||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="text-white">-< name >-</a>
|
||||
<span>
|
||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="mx-1 btn btn-dark">Open</a>
|
||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
||||
<span class="badge badge-primary badge-pill">Folder</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
-->
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script>
|
||||
|
||||
// empty for now
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
@ -66,18 +66,54 @@
|
||||
|
||||
</td>
|
||||
<td>{{ user.api_token }}</td>
|
||||
<td>{{ user.allowed_servers}}</td>
|
||||
<td><a href="/panel/edit_user?id={{user.id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||
<td>{{ [] }}</td>
|
||||
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fas fa-server"></i> Roles</h4>
|
||||
<div class="d-md-none">
|
||||
<small>Can't see everything on mobile?<br /> Try scrolling the table sideways.</small>
|
||||
</div>
|
||||
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> Add New Role</a></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Role</th>
|
||||
<th>Allowed Servers</th>
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for role in data['roles'] %}
|
||||
<tr>
|
||||
<td>{{ role.role_name }}</td>
|
||||
<td>{{ [] }}</td>
|
||||
<td><a href="/panel/edit_role?id={{role.role_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
159
app/frontend/templates/panel/panel_edit_role.html
Normal file
159
app/frontend/templates/panel/panel_edit_role.html
Normal file
@ -0,0 +1,159 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit Role{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
{% if data['new_role'] %}
|
||||
<h4 class="page-title">
|
||||
New Role
|
||||
<br />
|
||||
<small>RID: N/A</small>
|
||||
</h4>
|
||||
{% else %}
|
||||
<h4 class="page-title">
|
||||
Edit Role - {{ data['role']['role_name'] }}
|
||||
<br />
|
||||
<small>RID: {{ data['role']['role_id'] }}</small>
|
||||
</h4>
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_name'] }}&subpage=config" role="tab" aria-selected="true">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_name'] }}&subpage=other" role="tab" aria-selected="false">
|
||||
<i class="fas fa-folder-tree"></i>Other</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
{% if data['new_role'] %}
|
||||
<form class="forms-sample" method="post" action="/panel/add_role">
|
||||
{% else %}
|
||||
<form class="forms-sample" method="post" action="/panel/edit_role">
|
||||
{% end %}
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
|
||||
<input type="hidden" name="subpage" value="config">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="role_name">Role Name <small class="text-muted ml-1"> - What you wish to call this role</small> </label>
|
||||
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_membership">Servers <small class="text-muted ml-1"> - servers this role is allowed to access </small> </label>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Server Name</th>
|
||||
<th>Access?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for server in data['servers_all'] %}
|
||||
<tr>
|
||||
<td>{{ server['server_name'] }}</td>
|
||||
<td>
|
||||
{% if server['server_id'] in data['role']['servers'] %}
|
||||
<input type="checkbox" class="form-check-input" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" checked="" value="1">
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" value="1">
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2">Save</button>
|
||||
<button type="reset" class="btn btn-light">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Role Config Area</h4>
|
||||
<p class="card-description"> Here is where you can change the configuration of your role</p>
|
||||
<blockquote class="blockquote">
|
||||
<p class="mb-0">
|
||||
Created: {{ str(data['role']['created']) }}
|
||||
<br />
|
||||
Last updated: {{ str(data['role']['last_update']) }}
|
||||
<br />
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{% if data['new_role'] %}
|
||||
<a class="btn btn-sm btn-danger disabled">Delete Role</a><br />
|
||||
<small>You cannot delete something that does not yet exist</small>
|
||||
{% else %}
|
||||
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger">Delete Role</a>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
|
||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||
function getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
235
app/frontend/templates/panel/panel_edit_user.html
Normal file
235
app/frontend/templates/panel/panel_edit_user.html
Normal file
@ -0,0 +1,235 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit User{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
{% if data['new_user'] %}
|
||||
<h4 class="page-title">
|
||||
New User
|
||||
<br />
|
||||
<small>UID: N/A</small>
|
||||
</h4>
|
||||
{% else %}
|
||||
<h4 class="page-title">
|
||||
Edit User - {{ data['user']['user_id'] }}
|
||||
<br />
|
||||
<small>UID: {{ data['user']['user_id'] }}</small>
|
||||
</h4>
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/panel/edit_user?id={{ data['user']['username'] }}&subpage=config" role="tab" aria-selected="true">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['username'] }}&subpage=other" role="tab" aria-selected="false">
|
||||
<i class="fas fa-folder-tree"></i>Other</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
{% if data['new_user'] %}
|
||||
<form class="forms-sample" method="post" action="/panel/add_user">
|
||||
{% else %}
|
||||
<form class="forms-sample" method="post" action="/panel/edit_user">
|
||||
{% end %}
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
|
||||
<input type="hidden" name="subpage" value="config">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username">User Name <small class="text-muted ml-1"> - What you wish to call this user</small> </label>
|
||||
<input type="text" class="form-control" name="username" id="username" value="{{ data['user']['username'] }}" placeholder="User Name" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password0">Password <small class="text-muted ml-1"></small> </label>
|
||||
<input type="password" class="form-control" name="password0" id="password0" value="" placeholder="Password" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password1">Repeat Password <small class="text-muted ml-1"></small> </label>
|
||||
<input type="password" class="form-control" name="password1" id="password1" value="" placeholder="Repeat Password" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="role_membership">Roles <small class="text-muted ml-1"> - the roles this user is a member of</small> </label>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Role Name</th>
|
||||
<th>Member?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for role in data['roles_all'] %}
|
||||
<tr>
|
||||
<td>{{ role.role_name }}</td>
|
||||
<td>
|
||||
{% if role.role_id in data['user']['roles'] %}
|
||||
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" checked="" value="1">
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" value="1">
|
||||
{% end %}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_membership">Servers <small class="text-muted ml-1"> - servers this user is allowed to access </small> </label>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Server Name</th>
|
||||
<th>Access?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for server in data['servers_all'] %}
|
||||
<tr>
|
||||
<td>{{ server['server_name'] }}</td>
|
||||
<td>
|
||||
{% if server['server_id'] in data['user']['servers'] %}
|
||||
<input type="checkbox" class="form-check-input" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" checked="" value="1">
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" value="1">
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check-flat">
|
||||
<label for="enabled" class="form-check-label ml-4 mb-4">
|
||||
{% if data['user']['enabled'] %}
|
||||
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked="" value="1">Enabled
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">Enabled
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="regen_api" class="form-check-label ml-4 mb-4">
|
||||
{% if data['new_user'] %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" checked="" value="1" disabled >Regenerate API Key
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" value="1">Regenerate API Key
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="superuser" class="form-check-label ml-4 mb-4">
|
||||
{% if data['user']['superuser'] %}
|
||||
<input type="checkbox" class="form-check-input" id="superuser" name="superuser" checked="" value="1" disabled >Super User
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="superuser" name="superuser" value="1" disabled >Super User
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2">Save</button>
|
||||
<button type="reset" class="btn btn-light">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">User Config Area</h4>
|
||||
<p class="card-description"> Here is where you can change the configuration of your user</p>
|
||||
<blockquote class="blockquote">
|
||||
<p class="mb-0">
|
||||
Created: {{ str(data['user']['created']) }}
|
||||
<br />
|
||||
Last login: {{ str(data['user']['last_login']) }}
|
||||
<br />
|
||||
Last update: {{ str(data['user']['last_update']) }}
|
||||
<br />
|
||||
Last IP: {{ data['user']['last_ip'] }}
|
||||
<br />
|
||||
API Key: {{ data['user']['api_token'] }}
|
||||
<br />
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{% if data['new_user'] %}
|
||||
<a class="btn btn-sm btn-danger disabled">Delete User</a><br />
|
||||
<small>You cannot delete something that does not yet exist</small>
|
||||
{% elif data['user']['superuser'] %}
|
||||
<a class="btn btn-sm btn-danger disabled">Delete User</a><br />
|
||||
<small>You cannot delete a superuser</small>
|
||||
{% else %}
|
||||
<a href="/panel/remove_user?id={{ data['user']['user_id'] }}" class="btn btn-sm btn-danger">Delete User</a>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
|
||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||
function getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
@ -4,13 +4,14 @@
|
||||
<div class="card-body pt-3 pb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 mr-2">
|
||||
<b>Server Status:</b>
|
||||
{% if data['server_stats'][0]['running'] %}
|
||||
<span class="text-success">Online</span><br />
|
||||
<b>Server Started:</b> {{ data['server_stats'][0]['started'] }}
|
||||
<b>Server Status:</b> <span class="text-success">Online</span><br />
|
||||
<b>Server Started:</b> <span id="started">{{ data['server_stats'][0]['started'] }} (Server Time)</span><br />
|
||||
<b>Server Uptime:</b> <span id="uptime">Error Calculating</span>
|
||||
{% else %}
|
||||
<span class="text-danger">Offline</span><br />
|
||||
<b>Server Started:</b> Not Started
|
||||
<b>Server Status:</b> <span class="text-danger">Offline</span><br />
|
||||
<b>Server Started:</b> <span class="text-danger">Offline</span><br />
|
||||
<b>Server Uptime:</b> <span class="text-danger">Offline</span>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
@ -40,4 +41,91 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/assets/vendors/moment/moment.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script>
|
||||
|
||||
function durationToHumanizedString (duration) {
|
||||
duration._data.months += duration._data.years * 12;
|
||||
// 30.45833333333 = average month length, calculate with (31+28.5+31+30+31+30+31+31+30+31+30+31) / 12
|
||||
duration._data.days += duration._data.months * 30.45833333333;
|
||||
duration._data.hours += duration._data.days * 24;
|
||||
|
||||
let obj = {
|
||||
hours: Math.round(duration._data.hours),
|
||||
minutes: duration._data.minutes,
|
||||
seconds: duration._data.seconds
|
||||
}
|
||||
|
||||
output = Object.entries(obj)
|
||||
.map(([type, num]) => {
|
||||
// make them strings
|
||||
returnData = num + ' ' + type;
|
||||
// remove the s in the end if the data is -1 or 1
|
||||
if (num == -1 || num == 1)
|
||||
returnData = returnData.slice(0, -1)
|
||||
return returnData;
|
||||
})
|
||||
.map((v, i, a) => // example input: [1,2,3], output: "1, 2 and 3"
|
||||
v + (i !== a.length - 1
|
||||
? i !== a.length - 2
|
||||
? ', '
|
||||
: ' and '
|
||||
: '')).join('');
|
||||
return output;
|
||||
}
|
||||
|
||||
document.body.onload = (() => {
|
||||
|
||||
console.log('calculateTime');
|
||||
let uptime = document.querySelector('#uptime');
|
||||
let started = document.querySelector('#started');
|
||||
let startedUTC;
|
||||
let startedLocal;
|
||||
|
||||
if (started != null) {
|
||||
console.log('88', '{{ data['server_stats'][0]['started'] }}');
|
||||
{% if data['server_stats'][0]['started'] != 'False' %}
|
||||
startedUTC = '{{ (datetime.datetime.strptime(data['server_stats'][0]['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');
|
||||
|
||||
let browserUTCOffset = moment().utcOffset(); // This is in minutes
|
||||
|
||||
startedLocal = startedUTC.utcOffset(browserUTCOffset);
|
||||
startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
console.log('startedLocal', startedLocal);
|
||||
console.log('startedLocalFormatted', startedLocalFormatted);
|
||||
|
||||
started.textContent = startedLocalFormatted
|
||||
}
|
||||
|
||||
let nowServerTime = '{{ data['time'] }}';
|
||||
let startedServerTime = '{{ data['server_stats'][0]['started'] }}';
|
||||
|
||||
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);
|
||||
|
||||
uptime.textContent = durationToHumanizedString(diff);
|
||||
|
||||
console.log('startedLocal', startedLocal)
|
||||
if (startedLocal) {
|
||||
var uptimeLoop = setInterval(() => {
|
||||
var msdiff = moment()
|
||||
.diff(startedLocal);
|
||||
var diff = moment.duration(msdiff);
|
||||
|
||||
uptime.textContent = durationToHumanizedString(diff);
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
@ -112,6 +112,11 @@
|
||||
<input type="number" class="form-control" name="server_port" id="server_port" value="{{ data['server_stats'][0]['server_id']['server_port'] }}" step="1" max="65566" min="1" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="logs_delete_after">Remove Old Logs After <small class="text-muted ml-1"> - How many days will a log file has to be old to get deleted (0 is off)</small> </label>
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats'][0]['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" >
|
||||
</div>
|
||||
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats'][0]['server_id']['auto_start'] %}
|
||||
|
692
app/frontend/templates/panel/server_files.html
Normal file
692
app/frontend/templates/panel/server_files.html
Normal file
@ -0,0 +1,692 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Server Details{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
{% include "parts/details_stats.html %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
|
||||
<i class="fas fa-file-signature"></i>Terminal</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
|
||||
<i class="fas fa-file-signature"></i>Logs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
|
||||
<i class="fas fa-clock"></i>Schedule</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||
<i class="fas fa-save"></i>Backup</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
|
||||
<i class="fas fa-folder-tree"></i>Files</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<noscript>
|
||||
The file manager does not work without JavaScript
|
||||
</noscript>
|
||||
<div id="files-tree-nav" class="overlay">
|
||||
|
||||
<!-- Button to close the overlay navigation -->
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="document.getElementById('files-tree-nav').style.height = '0%';">×</a>
|
||||
|
||||
<!-- Overlay content -->
|
||||
<div id="files-tree-nav-content" class="overlay-content">
|
||||
<a onclick="createFileE(event)" href="javascript:void(0)" id="createFile" href="#">Create file</a>
|
||||
<a onclick="createDirE(event)" href="javascript:void(0)" id="createDir" href="#">Create directory</a>
|
||||
<a onclick="renameItemE(event)" href="javascript:void(0)" id="renameItem" href="#">Rename</a>
|
||||
<a onclick="deleteFileE(event)" href="javascript:void(0)" id="deleteFile" href="#">Delete</a>
|
||||
<a onclick="deleteDirE(event)" href="javascript:void(0)" id="deleteDir" href="#">Delete</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<style>
|
||||
/* The Overlay (background) */
|
||||
.overlay {
|
||||
/* Height & width depends on how you want to reveal the overlay (see JS below) */
|
||||
height: 0;
|
||||
width: 100vw;
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1031; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: rgb(0,0,0); /* Black fallback color */
|
||||
background-color: rgba(0,0,0, 0.9); /* Black w/opacity */
|
||||
overflow-x: hidden; /* Disable horizontal scroll */
|
||||
transition: 0.5s; /* 0.5 second transition effect to slide in or slide down the overlay (height or width, depending on reveal) */
|
||||
}
|
||||
|
||||
/* Position the content inside the overlay */
|
||||
.overlay-content {
|
||||
position: relative;
|
||||
top: 25%; /* 25% from the top */
|
||||
width: 100%; /* 100% width */
|
||||
text-align: center; /* Centered text/links */
|
||||
margin-top: 30px; /* 30px top margin to avoid conflict with the close button on smaller screens */
|
||||
}
|
||||
|
||||
/* The navigation links inside the overlay */
|
||||
.overlay a {
|
||||
padding: 8px;
|
||||
text-decoration: none;
|
||||
font-size: 36px;
|
||||
color: #818181;
|
||||
display: block; /* Display block instead of inline */
|
||||
transition: 0.3s; /* Transition effects on hover (color) */
|
||||
}
|
||||
|
||||
/* When you mouse over the navigation links, change their color */
|
||||
.overlay a:hover, .overlay a:focus {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Position the close button (top right corner) */
|
||||
.overlay .closebtn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 45px;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
/* When the height of the screen is less than 450 pixels, change the font-size of the links and position the close button again, so they don't overlap */
|
||||
@media screen and (max-height: 450px) {
|
||||
.overlay a {font-size: 20px}
|
||||
.overlay .closebtn {
|
||||
font-size: 40px;
|
||||
top: 15px;
|
||||
right: 35px;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
Files
|
||||
</div>
|
||||
<ul class="tree-nested" id="files-tree">
|
||||
<li>Error while getting files</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<style>
|
||||
/* Remove default bullets */
|
||||
.tree-view,
|
||||
.tree-nested {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.tree-caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.tree-caret .fa-folder {
|
||||
display: inline-block;
|
||||
}
|
||||
.tree-caret .fa-folder-open {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.tree-caret-down .fa-folder {
|
||||
display: none;
|
||||
}
|
||||
.tree-caret-down .fa-folder-open {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Hide the nested list */
|
||||
.tree-nested {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
Editing file <span id="editingFile"></span>
|
||||
<div id="editor" style="resize: both;">file_contents</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button onclick="setKeyboard(event.target)" class="btn btn-primary" data-handler-name="null">Default</button>
|
||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/vim">Vim</button>
|
||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/emacs">Emacs</button>
|
||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/sublime">Sublime</button>
|
||||
</div>
|
||||
<h3 id="file_warn"></h3>
|
||||
<button class="btn btn-success" onclick="save()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script src="/static/assets/vendors/ace-builds/src-min/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||
function getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
|
||||
let editor = ace.edit('editor');
|
||||
editor.setTheme('ace/theme/dracula');
|
||||
editor.session.setUseSoftTabs(true);
|
||||
|
||||
// mouseup = css resize end
|
||||
document.addEventListener("mouseup", function(e){
|
||||
editor.resize();
|
||||
});
|
||||
|
||||
let extensionChanges = [
|
||||
{
|
||||
regex: /^js$/,
|
||||
replaceWith: 'ace/mode/javascript'
|
||||
},
|
||||
{
|
||||
regex: /^py$/,
|
||||
replaceWith: 'ace/mode/python'
|
||||
},
|
||||
{
|
||||
regex: /^html$/,
|
||||
replaceWith: 'ace/mode/html'
|
||||
},
|
||||
{
|
||||
regex: /^yml$/,
|
||||
replaceWith: 'ace/mode/yaml'
|
||||
},
|
||||
{
|
||||
regex: /^yaml$/,
|
||||
replaceWith: 'ace/mode/yaml'
|
||||
},
|
||||
{
|
||||
regex: /^txt$/,
|
||||
replaceWith: 'ace/mode/text'
|
||||
},
|
||||
{
|
||||
regex: /^json$/,
|
||||
replaceWith: 'ace/mode/json'
|
||||
},
|
||||
{
|
||||
regex: /^java$/,
|
||||
replaceWith: 'ace/mode/java'
|
||||
},
|
||||
{
|
||||
regex: /^cpp$/,
|
||||
replaceWith: 'ace/mode/c_cpp'
|
||||
},
|
||||
{
|
||||
regex: /^css$/,
|
||||
replaceWith: 'ace/mode/css'
|
||||
},
|
||||
{
|
||||
regex: /^scss$/,
|
||||
replaceWith: 'ace/mode/scss'
|
||||
},
|
||||
{
|
||||
regex: /^sass$/,
|
||||
replaceWith: 'ace/mode/sass'
|
||||
},
|
||||
{
|
||||
regex: /^lua$/,
|
||||
replaceWith: 'ace/mode/lua'
|
||||
},
|
||||
{
|
||||
regex: /^php$/,
|
||||
replaceWith: 'ace/mode/php'
|
||||
},
|
||||
{
|
||||
regex: /^ps1$/,
|
||||
replaceWith: 'ace/mode/powershell'
|
||||
},
|
||||
{
|
||||
regex: /^svg$/,
|
||||
replaceWith: 'ace/mode/svg'
|
||||
},
|
||||
{
|
||||
regex: /^sh$/,
|
||||
replaceWith: 'ace/mode/sh'
|
||||
},
|
||||
{
|
||||
regex: /^xml$/,
|
||||
replaceWith: 'ace/mode/xml'
|
||||
},
|
||||
{
|
||||
regex: /^ts$/,
|
||||
replaceWith: 'ace/mode/typescript'
|
||||
}
|
||||
];
|
||||
|
||||
var editorEnabled = false;
|
||||
var filePath = '';
|
||||
|
||||
function clickOnFile(event) {
|
||||
editorEnabled = true;
|
||||
filePath = event.target.getAttribute('data-path');
|
||||
setFileName(event.target.innerText);
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/ajax/get_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}&file_path=' + encodeURIComponent(filePath),
|
||||
dataType: 'text',
|
||||
success: function (data) {
|
||||
console.log('Got File Contents From Server');
|
||||
editor.session.setValue(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function setFileName(name) {
|
||||
let fileName = name || 'default.txt';
|
||||
document.getElementById('editingFile').innerText = fileName;
|
||||
|
||||
if (fileName.match('.')) {
|
||||
|
||||
// The pop method removes and returns the last element.
|
||||
setMode(fileName
|
||||
.split('.')
|
||||
.pop()
|
||||
.replace('ace/mode/', ''));
|
||||
|
||||
} else {
|
||||
setMode('txt');
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = 'Warning: This is not a supported language';
|
||||
}
|
||||
}
|
||||
|
||||
setFileName();
|
||||
|
||||
function setMode (extension) {
|
||||
// if the extension matches with the RegEx it will return the replaceWith
|
||||
// property. else it will return the one it has. defaults to the extension.
|
||||
// this runs for each element in extensionChanges.
|
||||
let aceMode = extensionChanges.reduce((output, element) => {
|
||||
return extension.match(element.regex)
|
||||
? element.replaceWith
|
||||
: output;
|
||||
}, extension);
|
||||
|
||||
if (!aceMode.startsWith('ace/mode/')) {
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = 'Warning: This is not a supported language';
|
||||
} else {
|
||||
document
|
||||
.querySelector('#file_warn')
|
||||
.innerText = '';
|
||||
|
||||
console.log(aceMode || 'ace/mode/text');
|
||||
editor.session.setMode(aceMode || 'ace/mode/text');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function save() {
|
||||
let text = editor.session.getValue();
|
||||
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/save_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
file_contents: text,
|
||||
file_path: filePath
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createFile(parent, name, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/create_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
file_parent: parent,
|
||||
file_name: name
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createDir(parent, name, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/create_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
dir_parent: parent,
|
||||
dir_name: name
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function renameItem(path, name, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/rename_item?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
item_path: path,
|
||||
new_item_name: name
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function deleteFile(path, callback) {
|
||||
console.log('Deleting: ' + path)
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/del_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
file_path: path
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDir(path, callback) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/ajax/del_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
data: {
|
||||
dir_path: path
|
||||
},
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getTreeView() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: '/ajax/get_tree?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||
dataType: 'text',
|
||||
success: function(data){
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
|
||||
dataArr = data.split('\n');
|
||||
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
||||
text = dataArr.join('\n');
|
||||
|
||||
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;
|
||||
|
||||
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 setTreeViewContext() {
|
||||
var treeItems = document.getElementsByClassName('tree-ctx-item');
|
||||
|
||||
for (var i = 0; i < treeItems.length; i++) {
|
||||
var treeItem = treeItems[i];
|
||||
treeItem.addEventListener('contextmenu', function contextListener(event) {
|
||||
event.preventDefault();
|
||||
var ctxmenuPath = event.target.getAttribute('data-path');
|
||||
var ctxmenuName = event.target.getAttribute('data-name');
|
||||
if (!ctxmenuPath) {
|
||||
console.log({ 'event.target': event.target, ctxmenuPath });
|
||||
return;
|
||||
}
|
||||
$('#renameItem').show();
|
||||
|
||||
var isDir = event.target.classList.contains('tree-folder');
|
||||
$('#createFile').toggle(isDir);
|
||||
$('#createDir').toggle(isDir);
|
||||
$('#deleteDir').toggle(isDir);
|
||||
|
||||
var isFile = event.target.classList.contains('tree-file');
|
||||
$('#deleteFile').toggle(isFile);
|
||||
console.log({ 'event.target': event.target, isDir, isFile });
|
||||
|
||||
if(event.target.classList.contains('files-tree-title')) {
|
||||
$('#createFile').show();
|
||||
$('#createDir').show();
|
||||
$('#renameItem').hide();
|
||||
$('#deleteDir').hide();
|
||||
$('#deleteFile').hide();
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('files-tree-nav-content')
|
||||
.setAttribute('data-path', ctxmenuPath);
|
||||
|
||||
document.getElementById('files-tree-nav-content')
|
||||
.setAttribute('data-name', ctxmenuName);
|
||||
|
||||
document.getElementById("files-tree-nav").style.height = "100%";
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createFileE(event) {
|
||||
bootbox.prompt('What name do you want for the new file?', function(result) {
|
||||
path = event.target.parentElement.getAttribute('data-path');
|
||||
name = event.target.parentElement.getAttribute('data-name');
|
||||
if (!result) return;
|
||||
|
||||
createFile(path, result, function () {
|
||||
getTreeView()
|
||||
document.getElementById('files-tree-nav').style.height = '0%';
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function createDirE(event) {
|
||||
bootbox.prompt('What name do you want for the new directory?', function(result) {
|
||||
path = event.target.parentElement.getAttribute('data-path');
|
||||
name = event.target.parentElement.getAttribute('data-name');
|
||||
if (!result) return;
|
||||
|
||||
createDir(path, result, function () {
|
||||
getTreeView()
|
||||
document.getElementById('files-tree-nav').style.height = '0%';
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function renameItemE(event) {
|
||||
bootbox.prompt('What should the new name be?', function(result) {
|
||||
path = event.target.parentElement.getAttribute('data-path');
|
||||
name = event.target.parentElement.getAttribute('data-name');
|
||||
if (!result) return;
|
||||
|
||||
renameItem(path, result, function () {
|
||||
getTreeView()
|
||||
document.getElementById('files-tree-nav').style.height = '0%';
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function deleteFileE(event) {
|
||||
path = event.target.parentElement.getAttribute('data-path');
|
||||
name = event.target.parentElement.getAttribute('data-name');
|
||||
bootbox.confirm({
|
||||
size: "",
|
||||
title: "Are you sure you want to delete " + name + "?",
|
||||
closeButton: false,
|
||||
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes, I understand the consequences',
|
||||
className: 'btn-danger'
|
||||
},
|
||||
cancel: {
|
||||
label: 'No',
|
||||
className: 'btn-link'
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result) return;
|
||||
deleteFile(path, function () {
|
||||
getTreeView()
|
||||
document.getElementById('files-tree-nav').style.height = '0%';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDirE(event) {
|
||||
path = event.target.parentElement.getAttribute('data-path');
|
||||
name = event.target.parentElement.getAttribute('data-name');
|
||||
bootbox.confirm({
|
||||
size: "",
|
||||
title: "Are you sure you want to delete " + name + "?",
|
||||
closeButton: false,
|
||||
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes, I understand the consequences',
|
||||
className: 'btn-danger'
|
||||
},
|
||||
cancel: {
|
||||
label: 'No',
|
||||
className: 'btn-link'
|
||||
}
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result) return;
|
||||
deleteDir(path, function () {
|
||||
getTreeView()
|
||||
document.getElementById('files-tree-nav').style.height = '0%';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
function setKeyboard(target) {
|
||||
var handlerName = target.getAttribute('data-handler-name');
|
||||
if (handlerName == 'null') handlerName = null;
|
||||
editor.setKeyboardHandler(handlerName);
|
||||
|
||||
var nodes = target.parentNode.querySelectorAll("[data-handler-name]");
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].classList.remove('btn-primary');
|
||||
nodes[i].classList.add('btn-secondary');
|
||||
}
|
||||
|
||||
target.classList.remove('btn-secondary');
|
||||
target.classList.add('btn-primary');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
0
app/frontend/templates/public/404.html
Executable file → Normal file
0
app/frontend/templates/public/404.html
Executable file → Normal file
0
app/frontend/templates/public/error.html
Executable file → Normal file
0
app/frontend/templates/public/error.html
Executable file → Normal file
0
app/frontend/templates/public/login.html
Executable file → Normal file
0
app/frontend/templates/public/login.html
Executable file → Normal file
@ -140,7 +140,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg()">Import My Server!</button>
|
||||
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">Import My Server!</button>
|
||||
<button type="reset" class="btn btn-danger mr-2">Reset Form</button>
|
||||
|
||||
</form>
|
||||
@ -216,7 +216,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg()">Import My Server!</button>
|
||||
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">Import My Server!</button>
|
||||
<button type="reset" class="btn btn-danger mr-2">Reset Form</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -239,10 +239,10 @@
|
||||
});
|
||||
});
|
||||
|
||||
function wait_msg(){
|
||||
function wait_msg(importing){
|
||||
bootbox.alert({
|
||||
title: 'Downloading Server...',
|
||||
message: '<i class="fas fa-cloud-download"></i> Please be patient as we download the server'
|
||||
title: importing ? 'Importing Server...' : 'Downloading Server...',
|
||||
message: '<i class="fas fa-cloud-download"></i> Please be patient as we ' + (importing ? 'import' : 'download') + ' the server'
|
||||
});
|
||||
}
|
||||
|
||||
|
0
app/frontend/templates/setup/setup1.html
Executable file → Normal file
0
app/frontend/templates/setup/setup1.html
Executable file → Normal file
3
main.py
3
main.py
@ -44,7 +44,9 @@ def setup_logging(debug=False):
|
||||
logging_config = json.load(f)
|
||||
if debug:
|
||||
logging_config['loggers']['']['level'] = 'DEBUG'
|
||||
|
||||
logging.config.dictConfig(logging_config)
|
||||
|
||||
else:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.warning("Unable to read logging config from {}".format(logging_config_file))
|
||||
@ -74,6 +76,7 @@ if __name__ == '__main__':
|
||||
|
||||
# setting up the logger object
|
||||
logger = logging.getLogger(__name__)
|
||||
print("Logging set to: {} ".format(logger.level))
|
||||
|
||||
# print our pretty start message
|
||||
do_intro()
|
||||
|
Loading…
Reference in New Issue
Block a user